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

Vorbereitung 28.1.2021

parent a24dd28c
Branches
No related tags found
No related merge requests found
Showing
with 2904 additions and 0 deletions
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint32_t a;
uint64_t b;
uint8_t c;
} three_numbers;
int main (void)
{
three_numbers xyz = { 1819042120, 2410670883059281007, 0 };
printf ("%s\n", &xyz);
return 0;
}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strcpy (sls.first_name, "Sabine");
strcpy (sls.family_name, "Leutheusser-Schnarrenberger");
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int wings;
} with_wings;
typedef struct with_legs
{
int legs;
} with_legs;
int main (void)
{
animal *a[2];
animal duck;
a[0] = &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
a[0]->wings = 2;
animal cow;
a[1] = &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
a[1]->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int wings;
} with_wings;
typedef struct with_legs
{
int legs;
} with_legs;
int main (void)
{
animal *a[2];
animal duck;
a[0] = &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
((with_wings *) a[0])->wings = 2;
animal cow;
a[1] = &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
((with_legs *) a[1])->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
../common/hello-gtk.png
\ No newline at end of file
File added
% hp-20210128.pdf - Lecture Slides on Low-Level Programming
% Copyright (C) 2012, 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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: Objektorientierte Programmierung
\documentclass[10pt,t]{beamer}
\usepackage{pgslides}
\usepackage{rotating}
\usepackage{tikz}
\title{Hardwarenahe Programmierung}
\author{Prof.\ Dr.\ rer.\ nat.\ Peter Gerwinski}
\date{28.\ Januar 2021}
\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}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Algorithmen}
\item[\textbf{5}] \textbf{Hardwarenahe Programmierung}
\begin{itemize}
\item[5.1] Bit-Operationen
\item[5.2] I/O-Ports
\color{medgreen}
\item[5.3] Interrupts
\item[5.4] volatile-Variable
\item[5.6] Byte-Reihenfolge -- Endianness
\item[5.7] Binärdarstellung negativer Zahlen
\item[5.8] Speicherausrichtung -- Alignment
\end{itemize}
\item[\textbf{6}] \textbf{Objektorientierte Programmierung}
\begin{itemize}
\color{red}
\item[6.0] Dynamische Speicherverwaltung
\item[6.1] Konzepte und Ziele
\vspace*{-\smallskipamount}
\item[\dots]
% \item[6.2] Beispiel: Zahlen und Buchstaben
% \color{red}
% \item[6.3] Unions
% \item[6.4] Virtuelle Methoden
% \color{black}
% \item[6.5] Beispiel: Graphische Benutzeroberfläche (GUI)
% \item[6.6] Ausblick: C++
\end{itemize}
\item[\textbf{7}] \textbf{Datenstrukturen}
\vspace*{-1cm}
\end{itemize}
\end{frame}
\setcounter{section}{4}
\section{Hardwarenahe Programmierung}
\subsection{Bit-Operationen}
\subsubsection{Zahlensysteme}
\begin{frame}[fragile]
\showsubsubsection
\begin{tabular}{rlrlrc}
\qquad 000 & \bf 0 \hspace*{1.5cm} & 0000 & \bf 0 & \quad 1000 & \bf 8\\
001 & \bf 1 & 0001 & \bf 1 & 1001 & \bf 9\\
010 & \bf 2 & 0010 & \bf 2 & 1010 & \bf A\\
011 & \bf 3 & 0011 & \bf 3 & 1011 & \bf B\\[\smallskipamount]
100 & \bf 4 & 0100 & \bf 4 & 1100 & \bf C\\
101 & \bf 5 & 0101 & \bf 5 & 1101 & \bf D\\
110 & \bf 6 & 0110 & \bf 6 & 1110 & \bf E\\
111 & \bf 7 & 0111 & \bf 7 & 1111 & \bf F\\
\end{tabular}
\medskip
\begin{itemize}
\item
Oktal- und Hexadezimalzahlen lassen sich ziffernweise\\
in Binär-Zahlen umrechnen.
\item
Hexadezimalzahlen sind eine Kurzschreibweise für Binärzahlen,\\
gruppiert zu jeweils 4 Bits.
\item
Oktalzahlen sind eine Kurzschreibweise für Binärzahlen,\\
gruppiert zu jeweils 3 Bits.
\item
Trotz Taschenrechner u.\,ä.\ lohnt es sich,\\
die o.\,a.\ Umrechnungstabelle \textbf{auswendig} zu kennen.
\end{itemize}
\end{frame}
\subsubsection{Bit-Operationen in C}
\begin{frame}[fragile]
\showsubsubsection
\begin{tabular}{lll}
C-Operator & Verknüpfung & Anwendung \\[\smallskipamount]
\lstinline,&, & Und & Bits gezielt löschen \\
\lstinline,|, & Oder & Bits gezielt setzen \\
\lstinline,^, & Exklusiv-Oder & Bits gezielt invertieren \\
\lstinline,~, & Nicht & Alle Bits invertieren \\[\smallskipamount]
\lstinline,<<, & Verschiebung nach links & Maske generieren \\
\lstinline,>>, & Verschiebung nach rechts & Bits isolieren
\end{tabular}
\bigskip
Numerierung der Bits: von rechts ab 0
\medskip
\begin{tabular}{ll}
Bit Nr.\ 3 auf 1 setzen: &
\lstinline,a |= 1 << 3;, \\
Bit Nr.\ 4 auf 0 setzen: &
\lstinline,a &= ~(1 << 4);, \\
Bit Nr.\ 0 invertieren: &
\lstinline,a ^= 1 << 0;,
\end{tabular}
\smallskip
~~Abfrage, ob Bit Nr.\ 1 gesetzt ist:\quad
\lstinline{if (a & (1 << 1))}
\end{frame}
\nosectionnonumber{Aufgabe}
\begin{frame}[fragile]
\shownosectionnonumber
Schreiben Sie C-Funktionen, die ein "`Array von Bits"' realisieren, z.\,B.
\smallskip
\begin{tabular}[t]{ll}
\lstinline|void set_bit (int i);| & Bei Index $i$ auf 1 setzen \\
\lstinline|void clear_bit (int i);| & Bei Index $i$ auf 0 setzen \\
\lstinline|int get_bit (int i);| & Bei Index $i$ lesen
\end{tabular}
\medskip
Hinweise:
\begin{itemize}
\item
Die Größe des Bit-"`Arrays"' (z.\,B.\ 1000) dürfen Sie als \emph{vorher bekannt\/} voraussetzen.
\item
Sie benötigen ein Array, z.\,B.\ von \lstinline|char|- oder \lstinline|int|-Variablen.
\item
Sie benötigen eine Division (\lstinline|/|) sowie den Divisionsrest (Modulo: \lstinline|%|).
\end{itemize}
\bigskip
Diese Aufgabe berührt die Themen
"`Umgang mit Arrays und Zeigern"', "`Bit-Manipulation"'
und je nach Implementierung auch "`objektorientierte Programmierung"'.
Dies zu begründen, ist Teil der Aufgabe.
\end{frame}
\addtocounter{subsection}{1}
\subsection{Interrupts}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: eingebaute Uhr\hfill
\makebox(0,0)[tr]{%
\only<1->{\begin{minipage}[t]{4.7cm}
\vspace*{-0.3cm}%
statt Zählschleife (\lstinline{_delay_ms}):\\
Hauptprogramm kann\\
andere Dinge tun
\end{minipage}}%
}
\medskip
\begin{lstlisting}
#include <avr/interrupt.h>
...
ISR (TIMER0B_COMP_vect)
{
PORTD ^= 0x40;
}
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(1.9,3.1){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-1.4,-1.0);}}}
\put(2.0,3.2){\makebox(0,0)[l]{"`Dies ist ein Interrupt-Handler."'}}
\put(2.3,2.6){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-0.6,-0.55);}}}
\put(2.4,2.6){\makebox(0,0)[l]{Interrupt-Vektor darauf zeigen lassen}}
\end{picture}
Initialisierung über spezielle Ports:
\lstinline{TCCR0B}, \lstinline{TIMSK0}
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\vspace*{-2.5cm}\hfill
{\color{red}Herstellerspezifisch!}%
\hspace*{1cm}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: Taster\hfill
\makebox(0,0)[tr]{%
\begin{minipage}[t]{4.7cm}
\vspace*{-0.3cm}%
statt \newterm{Busy Waiting\/}:\\
Hauptprogramm kann\\
andere Dinge tun
\end{minipage}}
\medskip
\begin{lstlisting}
#include <avr/interrupt.h>
...
ISR (INT0_vect)
{
PORTD ^= 0x40;
}
\end{lstlisting}
\medskip
Initialisierung über spezielle Ports:
\lstinline{EICRA}, \lstinline{EIMSK}
\bigskip
\emph{Details: siehe Datenblatt und Schaltplan}
\vspace*{-2.5cm}\hfill
{\color{red}Herstellerspezifisch!}%
\hspace*{1cm}
\end{frame}
\subsection{volatile-Variable}
\begin{frame}[fragile]
\showsubsection
Externes Gerät ruft (per Stromsignal) Unterprogramm auf
Zeiger hinterlegen: "`Interrupt-Vektor"'
Beispiel: Taster
\vspace*{-2.5pt}
\begin{minipage}[t]{5cm}
\begin{onlyenv}<1>
\begin{lstlisting}[gobble=8]
¡#include <avr/interrupt.h>
...
uint8_t key_pressed = 0;
ISR (INT0_vect)
{
key_pressed = 1;
}¿
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2>
\begin{lstlisting}[gobble=8]
¡#include <avr/interrupt.h>
...
volatile uint8_t key_pressed = 0;
ISR (INT0_vect)
{
key_pressed = 1;
}¿
\end{lstlisting}
\end{onlyenv}
\end{minipage}\hfill
\begin{minipage}[t]{6cm}
\begin{lstlisting}[gobble=6]
¡int main (void)
{
...
while (1)
{
while (!key_pressed)
; /* just wait */
PORTD ^= 0x40;
key_pressed = 0;
}
return 0;
}¿
\end{lstlisting}
\end{minipage}
\pause
\begin{picture}(0,0)
\color{red}
\put(10.3,4.0){\makebox(0,0)[b]{\begin{minipage}{6cm}
\begin{center}
\textbf{volatile}:\\
Speicherzugriff\\
nicht wegoptimieren
\end{center}
\end{minipage}}}
\put(10.3,3.95){\makebox(0,0)[tr]{\tikz{\draw[-latex](0,0)--(-0.5,-0.9);}}}
\end{picture}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Was ist eigentlich \lstinline{PORTD}?
\bigskip
% \pause
\lstinline[style=cmd]{avr-gcc -Wall -Os -mmcu=atmega328p blink-3.c -E}
\bigskip
% \pause
\lstinline{PORTD = 0x01;}\\
\textarrow\quad
\lstinline[style=terminal]{(*(volatile uint8_t *)((0x0B) + 0x20)) = 0x01;}\\
% \pause
\begin{picture}(0,2)(0,-1.7)
\color{red}
\put(5.75,0.3){$\underbrace{\rule{2.95cm}{0pt}}_{\mbox{Zahl: \lstinline|0x2B|}}$}
% \pause
\put(1.55,0.3){$\underbrace{\rule{4.0cm}{0pt}}_{\mbox{\shortstack[t]{Umwandlung in Zeiger\\
auf \lstinline|volatile uint8_t|}}}$}
% \pause
\put(1.32,-1){\makebox(0,0)[b]{\tikz{\draw[-latex](0,0)--(0,1.3)}}}
\put(1.12,-1.1){\makebox(0,0)[tl]{Dereferenzierung des Zeigers}}
\end{picture}
% \pause
\textarrow\quad
\lstinline|volatile uint8_t|-Variable an Speicheradresse \lstinline|0x2B|
% \pause
\bigskip
\bigskip
\textarrow\quad
\lstinline|PORTA = PORTB = PORTC = PORTD = 0| ist eine schlechte Idee.
\end{frame}
\subsection{Byte-Reihenfolge -- Endianness}
\subsubsection{Konzept}
\begin{frame}[fragile]
\showsubsection
\showsubsubsection
Eine Zahl geht über mehrere Speicherzellen.\\
Beispiel: 16-Bit-Zahl in 2 8-Bit-Speicherzellen
\smallskip
Welche Bits liegen wo?
% \pause
\bigskip
$1027 = 1024 + 2 + 1 = 0000\,0100\,0000\,0011_2 = 0403_{16}$
% \pause
\bigskip
Speicherzellen:
\medskip
\begin{tabular}{|c|c|l}\cline{1-2}
\raisebox{-0.25ex}{04} & \raisebox{-0.25ex}{03} & \strut Big-Endian "`großes Ende zuerst"' \\\cline{1-2}
\multicolumn{2}{c}{} & für Menschen leichter lesbar \\
\multicolumn{3}{c}{} \\[-5pt]\cline{1-2}
\raisebox{-0.25ex}{03} & \raisebox{-0.25ex}{04} & \strut Little-Endian "`kleines Ende zuerst"' \\\cline{1-2}
\multicolumn{2}{c}{} & bei Additionen effizienter
\end{tabular}
% \pause
\medskip
\textarrow\ Geschmackssache
% \pause\\
\quad\textbf{\dots\ außer bei Datenaustausch!}
% \pause
% \bigskip
%
% Aber: nicht verwechseln! \qquad $0304_{16} = 772$
\end{frame}
\begin{frame}
\showsubsection
\showsubsubsection
Eine Zahl geht über mehrere Speicherzellen.\\
Beispiel: 16-Bit-Zahl in 2 8-Bit-Speicherzellen
\smallskip
Welche Bits liegen wo?
\medskip
\textarrow\ Geschmackssache\\
\textbf{\dots\ außer bei Datenaustausch!}
\begin{itemize}
\item
Dateiformate
\item
Datenübertragung
\end{itemize}
\end{frame}
\subsubsection{Dateiformate}
\begin{frame}
\showsubsection
\showsubsubsection
Audio-Formate: Reihenfolge der Bytes in 16- und 32-Bit-Zahlen
\begin{itemize}
\item
RIFF-WAVE-Dateien (\file{.wav}): Little-Endian
\item
Au-Dateien (\file{.au}): Big-Endian
% \pause
\item
ältere AIFF-Dateien (\file{.aiff}): Big-Endian
\item
neuere AIFF-Dateien (\file{.aiff}): Little-Endian
\end{itemize}
% \pause
\bigskip
Grafik-Formate: Reihenfolge der Bits in den Bytes
\begin{itemize}
\item
PBM-Dateien: Big-Endian\only<1->{, MSB first}
\item
XBM-Dateien: Little-Endian\only<1->{, LSB first}
\end{itemize}
\only<1->{MSB/LSB = most/least significant bit}
\smallskip
Verwechselungsgefahr: most/least significant byte
\end{frame}
\subsubsection{Datenübertragung}
\begin{frame}
\showsubsection
\showsubsubsection
\begin{itemize}
\item
RS-232 (serielle Schnittstelle): LSB first
\item
I$^2$C: MSB first
\item
USB: beides
% \pause
\medskip
\item
Ethernet: LSB first
\item
TCP/IP (Internet): Big-Endian
\end{itemize}
\end{frame}
\subsection{Binärdarstellung negativer Zahlen}
\begin{frame}[fragile]
\showsubsection
Speicher ist begrenzt!\\
\textarrow\ feste Anzahl von Bits
\medskip
8-Bit-Zahlen ohne Vorzeichen: \lstinline{uint8_t}\\
\textarrow\ Zahlenwerte von \lstinline{0x00} bis \lstinline{0xff} = 0 bis 255\\
% \pause
\textarrow\ 255 + 1 = 0
% \pause
\medskip
8-Bit-Zahlen mit Vorzeichen: \lstinline{int8_t}\\
\lstinline{0xff} = 255 ist die "`natürliche"' Schreibweise für $-1$.\\
% \pause
\textarrow\ Zweierkomplement
% \pause
\medskip
Oberstes Bit = 1: negativ\\
Oberstes Bit = 0: positiv\\
\textarrow\ 127 + 1 = $-128$
\end{frame}
\begin{frame}[fragile]
\showsubsection
Speicher ist begrenzt!\\
\textarrow\ feste Anzahl von Bits
\medskip
16-Bit-Zahlen ohne Vorzeichen:
\lstinline{uint16_t}\hfill\lstinline{uint8_t}\\
\textarrow\ Zahlenwerte von \lstinline{0x0000} bis \lstinline{0xffff}
= 0 bis 65535\hfill 0 bis 255\\
\textarrow\ 65535 + 1 = 0\hfill 255 + 1 = 0
\medskip
16-Bit-Zahlen mit Vorzeichen:
\lstinline{int16_t}\hfill\lstinline{int8_t}\\
\lstinline{0xffff} = 66535 ist die "`natürliche"' Schreibweise für $-1$.\hfill
\lstinline{0xff} = 255 = $-1$\\
\textarrow\ Zweierkomplement
\medskip
Oberstes Bit = 1: negativ\\
Oberstes Bit = 0: positiv\\
\textarrow\ 32767 + 1 = $-32768$
\bigskip
Literatur: \url{http://xkcd.com/571/}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Frage: \emph{Für welche Zahl steht der Speicherinhalt\,
\raisebox{2pt}{%
\tabcolsep0.25em
\begin{tabular}{|c|c|}\hline
\rule{0pt}{11pt}a3 & 90 \\\hline
\end{tabular}}
(hexadezimal)?}
% \pause
\smallskip
Antwort: \emph{Das kommt darauf an.} ;--)
% \pause
\medskip
Little-Endian:
\smallskip
\begin{tabular}{lrl}
als \lstinline,int8_t,: & $-93$ & (nur erstes Byte)\\
als \lstinline,uint8_t,: & $163$ & (nur erstes Byte)\\
als \lstinline,int16_t,: & $-28509$\\
als \lstinline,uint16_t,: & $37027$\\
\lstinline,int32_t, oder größer: & $37027$
& (zusätzliche Bytes mit Nullen aufgefüllt)
\end{tabular}
% \pause
\medskip
Big-Endian:
\smallskip
\begin{tabular}{lrl}
als \lstinline,int8_t,: & $-93$ & (nur erstes Byte)\\
als \lstinline,uint8_t,: & $163$ & (nur erstes Byte)\\
als \lstinline,int16_t,: & $-23664$\\
als \lstinline,uint16_t,: & $41872$\\ als \lstinline,int32_t,: & $-1550843904$ & (zusätzliche Bytes\\
als \lstinline,uint32_t,: & $2744123392$ & mit Nullen aufgefüllt)\\
als \lstinline,int64_t,: & $-6660823848880963584$\\
als \lstinline,uint64_t,: & $11785920224828588032$\\
\end{tabular}
\vspace*{-1cm}
\end{frame}
\subsection{Speicherausrichtung -- Alignment}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
#include <stdint.h>
uint8_t a;
uint16_t b;
uint8_t c;
\end{lstlisting}
% \pause
\bigskip
Speicheradresse durch 2 teilbar -- "`16-Bit-Alignment"'
\begin{itemize}
\item
2-Byte-Operation: effizienter
% \pause
\item
\dots\ oder sogar nur dann erlaubt
% \pause
\arrowitem
Compiler optimiert Speicherausrichtung
\end{itemize}
\medskip
% \pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t dummy;
uint16_t b;
uint8_t c;¿
\end{lstlisting}
\end{minipage}
% \pause
\begin{minipage}{3cm}
\begin{lstlisting}[gobble=6]
¡uint8_t a;
uint8_t c;
uint16_t b;¿
\end{lstlisting}
\end{minipage}
% \pause
\vspace{-1.75cm}
\strut\hfill
\begin{minipage}{6.5cm}
Fazit:
\begin{itemize}
\item
\textbf{Adressen von Variablen\\
sind systemabhängig}
\item
Bei Definition von Datenformaten\\
Alignment beachten \textarrow\ effizienter
\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}}}
\item[\textbf{2}] \textbf{Einführung in C}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Algorithmen}
\item[\textbf{5}] \textbf{Hardwarenahe Programmierung}
\begin{itemize}
\item[5.1] Bit-Operationen
\item[5.2] I/O-Ports
\color{medgreen}
\item[5.3] Interrupts
\item[5.4] volatile-Variable
\item[5.6] Byte-Reihenfolge -- Endianness
\item[5.7] Binärdarstellung negativer Zahlen
\item[5.8] Speicherausrichtung -- Alignment
\end{itemize}
\item[\textbf{6}] \textbf{Objektorientierte Programmierung}
\begin{itemize}
\color{red}
\item[6.0] Dynamische Speicherverwaltung
\item[6.1] Konzepte und Ziele
\vspace*{-\smallskipamount}
\item[\dots]
% \item[6.2] Beispiel: Zahlen und Buchstaben
% \color{red}
% \item[6.3] Unions
% \item[6.4] Virtuelle Methoden
% \color{black}
% \item[6.5] Beispiel: Graphische Benutzeroberfläche (GUI)
% \item[6.6] Ausblick: C++
\end{itemize}
\item[\textbf{7}] \textbf{Datenstrukturen}
\vspace*{-1cm}
\end{itemize}
\end{frame}
\section{Objektorientierte Programmierung}
\addtocounter{subsection}{-1}
\subsection{Dynamische Speicherverwaltung}
\begin{frame}[fragile]
\showsection
\showsubsection
\begin{itemize}
\item
Array: feste Anzahl von Elementen desselben Typs (z.\,B.\ 3 ganze Zahlen)
\item
Dynamisches Array: variable Anzahl von Elementen desselben Typs
\end{itemize}
\bigskip
\begin{lstlisting}
char *name[] = { "Anna", "Berthold", "Caesar" };
...
name[3] = "Dieter";
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(0,0){\line(3,1){3.5}}
\put(0,1){\line(3,-1){3.5}}
\end{picture}
\end{frame}
\begin{frame}[fragile]
\showsection
\showsubsection
\bigskip
\begin{lstlisting}
#include <stdlib.h>
...
char **name = malloc (3 * sizeof (char *));
/* Speicherplatz für 3 Zeiger anfordern */
...
free (name)
/* Speicherplatz freigeben */
\end{lstlisting}
\end{frame}
\subsection{Konzepte und Ziele}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
% Array: feste Anzahl von Elementen desselben Typs (z.\,B.\ 3 ganze Zahlen)
Array: Elemente desselben Typs (z.\,B.\ 3 ganze Zahlen)
% \item
% Dynamisches Array: variable Anzahl von Elementen desselben Typs
\item
Problem: Elemente unterschiedlichen Typs
\item
Lösung: den Typ des Elements zusätzlich speichern \textarrow\ \newterm{Objekt}
\item
Problem: Die Elemente sind unterschiedlich groß (Speicherplatz).
\item
Lösung: Im Array nicht die Objekte selbst speichern, sondern Zeiger darauf.
\end{itemize}
\begin{itemize}
\item
Funktionen, die mit dem Objekt arbeiten: \newterm{Methoden}
\begin{onlyenv}<1>
\item
Was die Funktion bewirkt,\\
hängt vom Typ des Objekts ab
\item
Realisierung über endlose \lstinline{if}-Ketten
\end{onlyenv}
\begin{onlyenv}<2>
\item
Was die Funktion bewirkt
\begin{picture}(0,0)
\color{red}
\put(-4.00,-0.05){\tikz{\draw[thick](0,0.25)--(3.75,-0.05);%
\draw[thick](-0.1,-0.05)--(3.75,0.3);}}
\end{picture}%
Welche Funktion aufgerufen wird,\\
hängt vom Typ des Objekts ab: \newterm{virtuelle Methode}
\item
Realisierung über endlose \lstinline{if}-Ketten%
\begin{picture}(0,0)
\color{red}
\put(-2.75,-0.05){\tikz{\draw[thick](0,0.25)--(2.5,-0.05);%
\draw[thick](-0.1,-0.05)--(2.5,0.3);}}
\put(1.5,-1.1){\begin{rotate}{7}\large\bf\textarrow\
kommt gleich
% nächste Woche
\end{rotate}}
\end{picture}
Zeiger, die im Objekt gespeichert sind\\
(Genaugenommen: Tabelle von Zeigern)
\end{onlyenv}
\end{itemize}
\end{frame}
\begin{frame}
\showsection
\showsubsection
\begin{itemize}
\item
Problem: Elemente unterschiedlichen Typs
\item
Lösung: den Typ des Elements zusätzlich speichern \textarrow\ \newterm{Objekt}
\item
\newterm{Methoden\/} und \newterm{virtuelle Methoden}
\end{itemize}
\begin{itemize}
\item
Zeiger auf verschiedene Strukturen\\
mit einem gemeinsamen Anteil von Datenfeldern\\
\textarrow\ "`verwandte"' \newterm{Objekte}, \newterm{Klassen} von Objekten
\item
Struktur, die \emph{nur\/} den gemeinsamen Anteil enthält\\
\textarrow\ "`Vorfahr"', \newterm{Basisklasse}, \newterm{Vererbung}
\item
% Explizite Typumwandlung eines Zeigers auf die Basisklasse\\
% in einen Zeiger auf die \newterm{abgeleitete Klasse}\\
% \textarrow\ Man kann ein Array unterschiedlicher Objekte\\
% \strut\phantom{\textarrow} in einer Schleife abarbeiten.\\
Zeiger auf die Basisklasse dürfen auf Objekte\\
der \newterm{abgeleiteten Klasse} zeigen\\
\textarrow\ \newterm{Polymorphie}
\end{itemize}
\end{frame}
\subsection{Beispiel: Zahlen und Buchstaben}
\begin{frame}[fragile]
\showsection
\showsubsection
\begin{center}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
} t_base;¿
\end{lstlisting}
\end{minipage}\\[0.5cm]
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
int content;
} t_integer;¿
\end{lstlisting}
\end{minipage}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
char *content;
} t_string;¿
\end{lstlisting}
\end{minipage}
\end{center}
\end{frame}
\begin{frame}[fragile]
\begin{center}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
} t_base;¿
\end{lstlisting}
\end{minipage}\\[0.5cm]
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
int content;
} t_integer;¿
\end{lstlisting}
\end{minipage}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
int type;
char *content;
} t_string;¿
\end{lstlisting}
\end{minipage}\\[0.7cm]
\begin{onlyenv}<1>
\begin{minipage}{8cm}
\begin{lstlisting}[gobble=10]
¡t_integer i = { 1, 42 };
t_string s = { 2, "Hello, world!" };
t_base *object[] = { (t_base *) &i, (t_base *) &s };¿
\end{lstlisting}
\end{minipage}%
\begin{picture}(0,0)
\color{red}
\put(-5.4,-0.8){\mbox{$\underbrace{\rule{1.45cm}{0pt}}_{\shortstack{\strut explizite\\Typumwandlung}}$}}
\end{picture}
\end{onlyenv}
% \begin{onlyenv}<2>
% \begin{minipage}{5cm}
% \begin{lstlisting}[gobble=10]
% ¡typedef union
% {
% t_base base;
% t_integer integer;
% t_string string;
% } t_object;¿
% \end{lstlisting}
% \end{minipage}
% \end{onlyenv}
\end{center}
\end{frame}
\subsection{Unions}
\begin{frame}[fragile]
\showsubsection
Variable teilen sich denselben Speicherplatz.
\medskip
\begin{minipage}[t]{3.7cm}
\begin{lstlisting}[gobble=6]
¡typedef union
{
int8_t i;
uint8_t u;
} num8_t;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{4.5cm}
\begin{lstlisting}[gobble=6]
¡int main (void)
{
num8_t test;
test.i = -1;
printf ("%d\n", test.u);
return 0;
}¿
\end{lstlisting}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Variable teilen sich denselben Speicherplatz.
\medskip
\begin{minipage}[t]{3.7cm}
\begin{lstlisting}[gobble=6]
¡typedef union
{
char s[8];
uint64_t x;
} num_char_t;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{4.5cm}
\begin{lstlisting}[gobble=6]
¡int main (void)
{
num_char_t test = { "Hello!" };
printf ("%lx\n", test.x);
return 0;
}¿
\end{lstlisting}
\end{minipage}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Variable teilen sich denselben Speicherplatz.
\medskip
\begin{minipage}[t]{3.7cm}
\begin{lstlisting}[gobble=6]
¡typedef union
{
t_base base;
t_integer integer;
t_string string;
} t_object;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{3.0cm}
\begin{lstlisting}[gobble=6]
¡typedef struct
{
int type;
} t_base;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{3.0cm}
\begin{lstlisting}[gobble=6]
¡typedef struct
{
int type;
int content;
} t_integer;¿
\end{lstlisting}
\end{minipage}%
\begin{minipage}[t]{3.0cm}
\begin{lstlisting}[gobble=6]
¡typedef struct
{
int type;
char *content;
} t_string;¿
\end{lstlisting}
\end{minipage}
\bigskip
\begin{center}
\begin{minipage}{8.5cm}
\begin{lstlisting}[gobble=8]
¡if (this->base.type == T_INTEGER)
printf ("Integer: %d\n", this->integer.content);
else if (this->base.type == T_STRING)
printf ("String: \"%s\"\n", this->string.content);¿
\end{lstlisting}
\end{minipage}
\end{center}
\end{frame}
\subsection{Virtuelle Methoden}
\begin{frame}[fragile]
\showsubsection
\begin{lstlisting}
void print_object (t_object *this)
{
if (this->base.type == T_INTEGER)
printf ("Integer: %d\n", this->integer.content);
else if (this->base.type == T_STRING)
printf ("String: \"%s\"\n", this->string.content);
}
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(9,1.7){\shortstack[l]{if-Kette:\\\strut wird unübersichtlich}}
\put(1,-2){\mbox{\textarrow}}
\put(0,-3){\mbox{Zeiger auf Funktionen}}
\end{picture}
\begin{lstlisting}[xleftmargin=4cm]
void print_integer (t_object *this)
{
printf ("Integer: %d\n", this->integer.content);
}
void print_string (t_object *this)
{
printf ("String: \"%s\"\n", this->string.content);
}
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]
\showsubsection
Zeiger auf Funktionen
\medskip
\begin{lstlisting}
void (* print) (t_object *this);
\end{lstlisting}
\begin{picture}(0,1.2)(0,-0.9)
\color{red}
\put(0.95,0.3){\mbox{$\underbrace{\rule{1cm}{0pt}}$}}
\put(0.2,-0.7){\shortstack{das, worauf print zeigt,\\ist eine Funktion}}
\end{picture}
\begin{itemize}
\item
Objekt enthält Zeiger auf Funktion
\begin{onlyenv}<1>
\medskip
\begin{lstlisting}[gobble=10]
typedef struct
{
void (* print) (union t_object *this);
int content;
} t_integer;
\end{lstlisting}
\end{onlyenv}
\begin{onlyenv}<2->
\vspace*{-3.5cm} % Why doesn't a picture environment work here??? :-(
\begin{lstlisting}[gobble=10,xleftmargin=5.5cm]
typedef struct
{
void (* print) (union t_object *this);
int content;
} t_integer;
\end{lstlisting}
\vspace*{0.85cm}
\bigskip
\smallskip
\end{onlyenv}
\pause
\item
Konstruktor initialisiert diesen Zeiger
\begin{onlyenv}<2>
\medskip
\begin{lstlisting}[gobble=10]
t_object *new_integer (int i)
{
t_object *p = malloc (sizeof (t_integer));
p->integer.print = print_integer;
p->integer.content = i;
return p;
}
\end{lstlisting}
\vspace*{-2cm}
\end{onlyenv}
\pause
\item
Aufruf: "`automatisch"' die richtige Funktion
\begin{onlyenv}<3>
\medskip
\begin{lstlisting}[gobble=10]
for (int i = 0; object[i]; i++)
object[i]->base.print (object[i]);
\end{lstlisting}
\end{onlyenv}
\pause
\medskip
\item
in größeren Projekten:\\
Objekt enthält Zeiger auf Tabelle von Funktionen
\end{itemize}
\end{frame}
\subsection{Beispiel: Graphische Benutzeroberfläche (GUI)}
\begin{frame}[fragile]
\showsubsection
\scriptsize
\begin{lstlisting}
#include <gtk/gtk.h>
int main (int argc, char **argv)
{
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
GtkWidget *label = gtk_label_new ("Hello, world!");
gtk_container_add (GTK_CONTAINER (vbox), label);
GtkWidget *button = gtk_button_new_with_label ("Quit");
g_signal_connect (button, "clicked", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_add (GTK_CONTAINER (vbox), button);
gtk_widget_show (button);
gtk_widget_show (label);
gtk_widget_show (vbox);
gtk_widget_show (window);
gtk_main ();
return 0;
}
\end{lstlisting}
\vspace*{-6cm}\strut\hfill
\includegraphics[scale=0.85]{hello-gtk.png}\\[2cm]
\begin{flushright}
\normalsize\bf Praktikumsversuch:\\
Objektorientiertes Zeichenprogramm
\end{flushright}
\end{frame}
\subsection{Ausblick: C++}
\begin{frame}[fragile]
\showsubsection
\begin{center}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
void (* print) (union t_object *this);
} t_base;¿
\end{lstlisting}
\end{minipage}\\[0.5cm]
\begin{minipage}{5.5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
void (* print) (...);
int content;
} t_integer;¿
\end{lstlisting}
\end{minipage}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡typedef struct
{
void (* print) (union t_object *this);
char *content;
} t_string;¿
\end{lstlisting}
\end{minipage}
\end{center}
\end{frame}
\begin{frame}[fragile]
\showsubsection
\begin{center}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡struct TBase
{
virtual void print (void);
};¿
\end{lstlisting}
\end{minipage}\\[0.5cm]
\begin{minipage}{5.5cm}
\begin{lstlisting}[gobble=8]
¡struct TInteger: public TBase
{
virtual void print (void);
int content;
};¿
\end{lstlisting}
\end{minipage}
\begin{minipage}{5cm}
\begin{lstlisting}[gobble=8]
¡struct TString: public TBase
{
virtual void print (void);
char *content;
};¿
\end{lstlisting}
\end{minipage}
\end{center}
\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}
\item[\textbf{3}] \textbf{Bibliotheken}
\item[\textbf{4}] \textbf{Algorithmen}
\item[\textbf{5}] \textbf{Hardwarenahe Programmierung}
\item[\textbf{6}] \textbf{Objektorientierte Programmierung}
\begin{itemize}
\color{medgreen}
\item[6.0] Dynamische Speicherverwaltung
\item[6.1] Konzepte und Ziele
\item[6.2] Beispiel: Zahlen und Buchstaben
\item[6.3] Unions
\item[6.4] Virtuelle Methoden
\item[6.5] Beispiel: Graphische Benutzeroberfläche (GUI)
\item[6.6] Ausblick: C++
\end{itemize}
\item[\textbf{7}] \textbf{Datenstrukturen}
\vspace*{-1cm}
\end{itemize}
\end{frame}
\end{document}
File added
% hp-musterloesung-20210128.pdf - Solutions to the Exercises on Low-Level Programming
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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: Speicherformate von Zahlen, Personen-Datenbank, objektorientierte Tier-Datenbank
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
\section*{Hardwarenahe Programmierung\\
Musterlösung zu den Übungsaufgaben -- 28.\ Januar 2021}
\exercise{Speicherformate von Zahlen}
Wir betrachten das folgende Programm (\gitfile{hp}{2020ws/20210128}{aufgabe-1.c}):
\begin{lstlisting}
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint32_t a;
uint64_t b;
uint8_t c;
} three_numbers;
int main (void)
{
three_numbers xyz = { 1819042120, 2410670883059281007, 0 };
printf ("%s\n", &xyz);
return 0;
}
\end{lstlisting}
Das Programm wird für einen 32-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m32} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 32-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall -m32 aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function "main":
aufgabe-2.c:14:13: warning: format "%s" expects argument of type "char *", but
argument 2 has type "three_numbers * {aka struct <anonymous> *}" [-Wformat=]
printf ("%s\n", &xyz);
^
$ ¡./aufgabe-2¿
Hallo, Welt!
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die beim Compilieren auftretende Warnung.
\points{2}
\item
Erklären Sie die Ausgabe des Programms.
\points{4}
\item
Welche Endianness hat der verwendete Rechner?
Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?
\points{2}
\item
Dasselbe Programm wird nun für einen 64-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m64} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 64-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal,gobble=8]
$ ¡gcc -Wall -m64 aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function "main":
aufgabe-2.c:14:13: warning: format "%s" expects argument of type "char *",
but argument 2 has type "three_numbers * {aka struct <anonymous> *}"
[-Wformat=]
printf ("%s\n", &xyz);
^
$ ¡./aufgabe-2¿
Hall5V
\end{lstlisting}
(Es ist möglich, daß die konkrete Ausgabe auf Ihrem Rechner anders aussieht.)\par
Erklären Sie die geänderte Ausgabe des Programms.
\points{3}
\end{enumerate}
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Erklären Sie die beim Compilieren auftretende Warnung.}
Die Funktion \lstinline{printf()} mit der Formatspezifikation \lstinline{%s}
erwartet als Parameter einen String, d.\,h.\ einen Zeiger auf \lstinline{char}.
Die Adresse (\lstinline{&}) der Variablen \lstinline{xyz}
ist zwar ein Zeiger, aber nicht auf \lstinline{char},
sondern auf einen \lstinline{struct} vom Typ \lstinline{three_numbers}.
Eine implizite Umwandlung des Zeigertyps ist zwar möglich,
aber normalerweise nicht das, was man beabsichtigt.
\item
\textbf{Erklären Sie die Ausgabe des Programms.}
Ein String in C ist ein Array von \lstinline{char}s
bzw.\ ein Zeiger auf \lstinline{char}.
Da die Funktion \lstinline{printf()} mit der Formatspezifikation \lstinline{%s}
einen String erwartet, wird sie das, worauf der übergebene Zeiger zeigt,
als ein Array von \lstinline{char}s interpretieren.
Ein \lstinline{char} entspricht einer 8-Bit-Speicherzelle.
Um die Ausgabe des Programms zu erklären, müssen wir daher
die Speicherung der Zahlen in den einzelnen 8-Bit-Speicherzellen betrachten.
Hierfür wandeln wir zunächst die Zahlen von Dezimal nach Hexadezimal um.
Sofern nötig (hier nicht der Fall) füllen wir von links mit Nullen auf,
um den gesamten von der Variablen belegten Speicherplatz zu füllen
(hier: 32 Bit, 64 Bit, 8 Bit).
Jeweils 2 Hex-Ziffern stehen für 8 Bit.
\begin{center}
\begin{tabular}{rcl}
dezimal & & hexadezimal \\[\smallskipamount]
1\,819\,042\,120 & = & 6C\,6C\,61\,48 \\
2\,410\,670\,883\,059\,281\,007 & = & 21\,74\,6C\,65\,57\,20\,2C\,6F \\
0 & = & 00
\end{tabular}
\end{center}
Die Anordnung dieser 8-Bit-Zellen im Speicher lautet
\textbf{auf einem Big-Endian-Rechner} wie folgt:
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
\raisebox{0.5ex}{\strut}
6C & 6C & 61 & 48 &
21 & 74 & 6C & 65 & 57 & 20 & 2C & 6F &
00
\\\hline
\end{tabular}\\[-4.2ex]
\kern1.7em%
$\underbrace{\rule{9.8em}{0pt}}_{\mbox{\lstinline{a}}}$\kern1pt%
$\underbrace{\rule{18.8em}{0pt}}_{\mbox{\lstinline{b}}}$\kern1pt%
$\underbrace{\rule{2.2em}{0pt}}_{\mbox{\lstinline{c}}}$%
\end{center}
\textbf{Auf einem Little-Endian-Rechner} lautet sie hingegen:
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
% \raisebox{0.5ex}{\strut}
% H & a & l & l &
% o & , & & W & e & l & t & ! & \\\hline
\raisebox{0.5ex}{\strut}
48 & 61 & 6C & 6C &
6F & 2C & 20 & 57 & 65 & 6C & 74 & 21 &
00
\\\hline
\end{tabular}\\[-4.2ex]
\kern1.7em%
$\underbrace{\rule{9.8em}{0pt}}_{\mbox{\lstinline{a}}}$\kern1pt%
$\underbrace{\rule{18.8em}{0pt}}_{\mbox{\lstinline{b}}}$\kern1pt%
$\underbrace{\rule{2.2em}{0pt}}_{\mbox{\lstinline{c}}}$%
\end{center}
Anhand einer ASCII-Tabelle erkennt man,
daß die Big-Endian-Variante dem String \lstinline{"llaH!tleW ,o"}
und die Little-Endian-Variante dem String \lstinline{"Hallo, Welt!"}
entspricht -- jeweils mit einem Null-Symbol am Ende,
das von der Variablen \lstinline{c} herrührt.
Auf einem Little-Endian-Rechner wird daher
\lstinline[style=terminal]{Hallo, Welt!} ausgegeben.
\item
\textbf{Welche Endianness hat der verwendete Rechner?}
Little-Endian (Begründung siehe oben)
\textbf{Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?}
\lstinline[style=terminal]{llaH!tleW ,o} (Begründung siehe oben)
\item
\textbf{Dasselbe Programm wird nun für einen 64-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m64} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 64-Bit-Prozessor erzeugt.)}
\begin{lstlisting}[style=terminal,gobble=8]
$ ¡gcc -Wall -m64 aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function "main":
aufgabe-2.c:14:13: warning: format "%s" expects argument of type "char *",
but argument 2 has type "three_numbers * {aka struct <anonymous> *}"
[-Wformat=]
printf ("%s\n", &xyz);
^
$ ¡./aufgabe-2¿
Hall5V
\end{lstlisting}
\textbf{(Es ist möglich, daß die konkrete Ausgabe auf Ihrem Rechner anders aussieht.)}\par
\textbf{Erklären Sie die geänderte Ausgabe des Programms.}
\goodbreak
Auf einem 64-Bit-Rechner hat eine 64-Bit-Variable
ein \textbf{64-Bit-Alignment},
d.\,h.\ ihre Speicheradresse muß durch 8 teilbar sein.
Der Compiler legt die Variablen daher wie folgt im Speicher ab (Little-Endian):
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
\raisebox{0.5ex}{\strut}
48 & 61 & 6C & 6C & ?? & ?? & ?? & ?? &
6F & 2C & 20 & 57 & 65 & 6C & 74 & 21 &
00
\\\hline
\end{tabular}\\[-4.2ex]
\kern1.7em%
$\underbrace{\rule{9.8em}{0pt}}_{\mbox{\lstinline{a}}}$\kern1pt%
$\underbrace{\rule{9.1em}{0pt}}_{\mbox{Füll-Bytes}}$\kern1pt%
$\underbrace{\rule{18.8em}{0pt}}_{\mbox{\lstinline{b}}}$\kern1pt%
$\underbrace{\rule{2.2em}{0pt}}_{\mbox{\lstinline{c}}}$%
\end{center}
Der Inhalt der Füll-Bytes ist undefiniert.
Im Beispiel aus der Aufgabenstellung entsteht hier die Ausgabe
\lstinline[style=terminal]{5V}, was den (zufälligen) hexadezimalen Werten
35 56 entspricht:
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|}\hline
\raisebox{0.5ex}{\strut}
48 & 61 & 6C & 6C & 35 & 56 & 00 & ?? &
6F & 2C & 20 & 57 & 65 & 6C & 74 & 21 &
00
\\\hline
\end{tabular}\\[-4.2ex]
\kern1.7em%
$\underbrace{\rule{9.8em}{0pt}}_{\mbox{\lstinline{a}}}$\kern1pt%
$\underbrace{\rule{9.1em}{0pt}}_{\mbox{Füll-Bytes}}$\kern1pt%
$\underbrace{\rule{18.8em}{0pt}}_{\mbox{\lstinline{b}}}$\kern1pt%
$\underbrace{\rule{2.2em}{0pt}}_{\mbox{\lstinline{c}}}$%
\end{center}
Da danach die Ausgabe aufhört, muß an der nächsten Stelle
ein Null-Symbol stehen, das das Ende des Strings anzeigt.
Der Inhalt der darauf folgenden Speicherzelle bleibt unbekannt.
\end{enumerate}
\exercise{Personen-Datenbank}
Wir betrachten das folgende Programm (\gitfile{hp}{2020ws/20210128}{aufgabe-2.c}):
\begin{lstlisting}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strcpy (sls.first_name, "Sabine");
strcpy (sls.family_name, "Leutheusser-Schnarrenberger");
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
\end{lstlisting}
Die Standard-Funktion \lstinline{strcpy()} bewirkt ein Kopieren eines Strings
von rechts nach links, hier also z.\,B.\ die Zuweisung der String-Konstanten
\lstinline{"Sabine"} an die String-Variable \lstinline{sls.first_name[]}.
Das Programm wird für einen 32-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m32} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 32-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall -O -m32 aufgabe-2.c -o aufgabe-2¿
$ ¡./aufgabe-2¿
Sabine Leutheusser-Schnarrenberger wurde am 110.98.1701278309 geboren.
Speicherzugriffsfehler
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die Ausgabe des Programms einschließlich der Zahlenwerte.
\points{4}
\item
Welche Endianness hat der verwendete Rechner?
Begründen Sie Ihre Antwort.
\points{1}
\item
Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?
\points{2}
\item
Erklären Sie den Speicherzugriffsfehler.
(Es kann sein, daß sich der Fehler auf Ihrem Rechner nicht bemerkbar macht.
Er ist aber trotzdem vorhanden.)
\points{2}
\end{enumerate}
\goodbreak
\solution
\begin{enumerate}[\quad(a)]
\item
\textbf{Erklären Sie die Ausgabe des Programms einschließlich der Zahlenwerte.}
Der String \lstinline{"Leutheusser-Schnarrenberger"}
hat 27 Zeichen und daher mehr als die in der Variablen
\lstinline{sls.family_name} vorgesehenen 20 Zeichen.
Das \lstinline{"nberger"} paßt nicht mehr in die String-Variable.
Die Zuweisung \lstinline{strcpy (sls.family_name, "Leutheusser-Schnarrenberger")}
überschreibt daher 8 Speicherzellen außerhalb der String-Variablen
\lstinline{sls.family_name} mit dem String \lstinline{"nberger"}
(7 Buchstaben zzgl.\ String-Ende-Symbol) -- und damit insbesondere die Variablen
\lstinline{sls.day}, \lstinline{sls.month} und \lstinline{sls.year}.
Die überschriebenen Speicherzellen sehen demnach folgendermaßen aus:
\begin{center}
\begin{picture}(8,1.5)(0,-0.5)
\put(0,0){\line(1,0){8}}
\put(0,1){\line(1,0){8}}
\multiput(0,0)(1,0){9}{\line(0,1){1}}
\put(0.4,0.38){\lstinline{n}}
\put(1.4,0.38){\lstinline{b}}
\put(2.4,0.38){\lstinline{e}}
\put(3.4,0.38){\lstinline{r}}
\put(4.4,0.38){\lstinline{g}}
\put(5.4,0.38){\lstinline{e}}
\put(6.4,0.38){\lstinline{r}}
\put(7.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(0.5,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{0.95cm}{0pt}}_{\mbox{\lstinline{day}}}$}}
\put(1.5,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{0.95cm}{0pt}}_{\mbox{\lstinline{month}}}$}}
\put(4.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{3.95cm}{0pt}}_{\mbox{\lstinline{year}}}$}}
\put(7.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{?}}$}}
\end{picture}
\end{center}
("`?"' steht für zwei Speicherzellen, von denen wir nicht wissen,
wofür sie genutzt werden.)
Wenn wir die ASCII-Zeichen in Hexadezimalzahlen umrechnen, entspricht dies:
\begin{center}
\begin{picture}(7,1.5)(0,-0.5)
\put(0,0){\line(1,0){8}}
\put(0,1){\line(1,0){8}}
\multiput(0,0)(1,0){9}{\line(0,1){1}}
\put(0.5,0.5){\makebox(0,0){\lstinline{0x6e}}}
\put(1.5,0.5){\makebox(0,0){\lstinline{0x62}}}
\put(2.5,0.5){\makebox(0,0){\lstinline{0x65}}}
\put(3.5,0.5){\makebox(0,0){\lstinline{0x72}}}
\put(4.5,0.5){\makebox(0,0){\lstinline{0x67}}}
\put(5.5,0.5){\makebox(0,0){\lstinline{0x65}}}
\put(6.5,0.5){\makebox(0,0){\lstinline{0x72}}}
\put(7.5,0.5){\makebox(0,0){\lstinline{0x00}}}
\put(0.5,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{0.95cm}{0pt}}_{\mbox{\lstinline{day}}}$}}
\put(1.5,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{0.95cm}{0pt}}_{\mbox{\lstinline{month}}}$}}
\put(4.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{3.95cm}{0pt}}_{\mbox{\lstinline{year}}}$}}
\put(7.0,-0.1){\makebox(0,0)[t]{$\underbrace{\rule{1.95cm}{0pt}}_{\mbox{?}}$}}
\end{picture}
\end{center}
Dies entspricht bereits genau den Werten \lstinline{110} und \lstinline{98}
für die Variablen \lstinline{sls.day} bzw.\ \lstinline{sls.month}.
Für die Variable \lstinline{sls.year} müssen wir ihre vier Speicherzellen
unter der Berücksichtigung der Endianness des Rechners zusammenziehen.
Für Big-Endian ergibt dies \lstinline{0x65726765 == 1701996389}.
Für Little-Endian ergibt sich der Wert \lstinline{0x65677265 == 1701278309},
der auch in der Ausgabe des Programms auftaucht.
\item
\textbf{Welche Endianness hat der verwendete Rechner?
Begründen Sie Ihre Antwort.}
Wie in (a) begründet, ergibt sich die Ausgabe von
\lstinline[style=terminal]{1701278309} für das Jahr
aus dem Speicherformat Little-Endian.
\item
\textbf{Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?}
Wie in (a) begründet, ergäbe sich aus dem Speicherformat Big-Endian
die Ausgabe von \lstinline[style=terminal]{1701996389} für das Jahr.
\item
\textbf{Erklären Sie den Speicherzugriffsfehler.
(Es kann sein, daß sich der Fehler auf Ihrem Rechner nicht bemerkbar macht.
Er ist aber trotzdem vorhanden.)}
Die zwei in (a) mit "`?"' bezeichneten Speicherzellen
wurden ebenfalls überschrieben.
Dies ist in der Ausgabe zunächst nicht sichtbar,
bewirkt aber später den Speicherzugriffsfehler.
(Tatsächlich handelt es sich bei den überschriebenen Speicherzellen
um einen Teil der Rücksprungadresse, die \lstinline{main()} verwendet,
um mit \lstinline{return 0} an das Betriebssystem zurückzugeben.)
\end{enumerate}
\textbf{Hinweis 1:}
Um auf einen solchen Lösungsweg zu kommen, wird empfohlen,
"`geheimnisvolle"' Zahlen nach hexadezimal umzurechnen
und in Speicherzellen (Zweiergruppen von Hex-Ziffern) zu zerlegen.
Oft erkennt man dann direkt ASCII-Zeichen:
Großbuchstaben beginnen mit der Hex-Ziffer \lstinline{4} oder \lstinline{5},
Kleinbuchstaben mit \lstinline{6} oder \lstinline{7}.
\textbf{Hinweis 2:}
Um derartige Programmierfehler in der Praxis von vorneherein zu vermeiden,
wird empfohlen, anstelle von \lstinline{strcpy()}
grundsätzlich die Funktion \lstinline{strncpy()} zu verwenden.
Diese erwartet einen zusätzlichen Parameter,
der die maximal zulässige Länge des Strings enthält.
Ohne einen derartigen expliziten Parameter kann die Funktion nicht wissen,
wie lang die Variable ist, in der der String gespeichert werden soll.
\exercise{Objektorientierte Tier-Datenbank}
Das unten dargestellte Programm (Datei: \gitfile{hp}{2020ws/20210128}{aufgabe-3a.c})
soll Daten von Tieren verwalten.
Beim Compilieren erscheinen die folgende Fehlermeldungen:
\begin{lstlisting}[style=terminal]
$ ¡gcc -std=c99 -Wall -O aufgabe-2a.c -o aufgabe-2a¿
aufgabe-2a.c: In function 'main':
aufgabe-2a.c:31: error: 'animal' has no member named 'wings'
aufgabe-2a.c:37: error: 'animal' has no member named 'legs'
\end{lstlisting}
Der Programmierer nimmt die auf der nächsten Seite in Rot dargestellten Ersetzungen vor\\
(Datei: \gitfile{hp}{2020ws/20210128}{aufgabe-3b.c}).
Daraufhin gelingt das Compilieren, und die Ausgabe des Programms lautet:
\begin{lstlisting}[style=terminal]
$ ¡gcc -std=c99 -Wall -O aufgabe-2b.c -o aufgabe-2b¿
$ ¡./aufgabe-2b¿
A duck has 2 legs.
Error in animal: cow
\end{lstlisting}
\begin{itemize}
\item[(a)]
Erklären Sie die o.\,a.\ Compiler-Fehlermeldungen.
\points{2}
\item[(b)]
Wieso verschwinden die Fehlermeldungen nach den o.\,a.\ Ersetzungen?
\points{3}
\item[(c)]
Erklären Sie die Ausgabe des Programms.
\points{5}
\item[(d)]
Beschreiben Sie -- in Worten und/oder als C-Quelltext -- einen Weg,
das Programm so zu berichtigen, daß es die Eingabedaten
(``A duck has 2 wings. A cow has 4 legs.'') korrekt speichert und ausgibt.\\
\points{4}
% \item[(e)]
% Schreiben Sie das Programm so um,
% daß es keine expliziten Typumwandlungen mehr benötigt.\par
% Hinweis: Verwenden Sie \lstinline{union}.
% \points{4}
% \item[(f)]
% Schreiben Sie das Programm weiter um,
% so daß es die Objektinstanzen \lstinline{duck} und \lstinline{cow}
% dynamisch erzeugt.\par
% Hinweis: Verwenden Sie \lstinline{malloc()} und schreiben Sie Konstruktoren.
% \points{4}
% \item[(g)]
% Schreiben Sie das Programm weiter um,
% so daß die Ausgabe nicht mehr direkt im Hauptprogramm erfolgt,
% sondern stattdessen eine virtuelle Methode \lstinline{print()}
% aufgerufen wird.\par
% Hinweis: Verwenden Sie in den Objekten Zeiger auf Funktionen,
% und initialisieren Sie diese in den Konstruktoren.
% \points{4}
\end{itemize}
\begin{minipage}[t]{0.34\textwidth}
\begin{lstlisting}[gobble=6,xleftmargin=0pt]
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int wings;
} with_wings;
typedef struct with_legs
{
int legs;
} with_legs;
\end{lstlisting}
\end{minipage}\hfill
\begin{minipage}[t]{0.65\textwidth}
\begin{lstlisting}[gobble=6,xleftmargin=0pt]
int main (void)
{
animal *a[2];
animal duck;
a[0] = &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
a[0]->wings = 2;
animal cow;
a[1] = &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
a[1]->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(3.7,6.207){\vector(-1,0){0.7}}
\put(3.8,6.207){\makebox(0,0)[l]{\lstinline[basicstyle=\color{red}]{((with_legs *) a[1])->legs = 4;}}}
\put(4.0,8.735){\vector(-1,0){0.7}}
\put(4.1,8.735){\makebox(0,0)[l]{\lstinline[basicstyle=\color{red}]{((with_wings *) a[0])->wings = 2;}}}
\end{picture}
\end{minipage}
\solution
\begin{itemize}
\item[(a)]
\textbf{Erklären Sie die o.\,a.\ Compiler-Fehlermeldungen.}
\lstinline{a[0]} und \lstinline{a[1]}
sind gemäß der Deklaration \lstinline{animal *a[2]}
Zeiger auf Variablen vom Typ \lstinline{animal} (ein \lstinline{struct}).
Wenn man diesen Zeiger dereferenziert (\lstinline{->}),
erhält man eine \lstinline{animal}-Variable.
Diese enthält keine Datenfelder \lstinline{wings} bzw.\ \lstinline{legs}.
\item[(b)]
\textbf{Wieso verschwinden die Fehlermeldungen nach den o.\,a.\ Ersetzungen?}
Durch die \emph{explizite Typumwandlung des Zeigers\/}
erhalten wir einen Zeiger auf eine \lstinline{with_wings}-
bzw.\ auf eine \lstinline{with_legs}-Variable.
Diese enthalten die Datenfelder \lstinline{wings} bzw.\ \lstinline{legs}.
\item[(c)]
\textbf{Erklären Sie die Ausgabe des Programms.}
Durch die explizite Typumwandlung des Zeigers
zeigt \lstinline{a[0]} auf eine \lstinline{with_wings}-Variable.
Diese enthält nur ein einziges Datenfeld \lstinline{wings},
das an genau derselben Stelle im Speicher liegt
wie \lstinline{a[0]->type}, also das Datenfeld \lstinline{type}
der \lstinline{animal}-Variable, auf die der Zeiger \lstinline{a[0]} zeigt.
Durch die Zuweisung der Zahl \lstinline{2}
an \lstinline{((with_wings *) a[0])->wings}
überschreiben wir also \lstinline{a[0]->type},
so daß das \lstinline{if} in der \lstinline{for}-Schleife
\lstinline{a[0]} als \lstinline{WITH_LEGS} erkennt.
Bei der Ausgabe \lstinline[style=terminal]{A duck has 2 legs.}
wird das Datenfeld \lstinline{((with_legs *)a[0])->legs} als Zahl ausgegeben.
Dieses Datenfeld befindet sich in denselben Speicherzellen wie
\lstinline{a[0]->type} und \lstinline{((with_wings *)} % :-(
\lstinline{a[0]->wings}
und hat daher ebenfalls den Wert 2.
Auf die gleiche Weise überschreiben wir durch die Zuweisung der Zahl 4
an \lstinline{((with_legs *) a[1])->legs}
das Datenfeld \lstinline{a[0]->type},
so daß das \lstinline{if} in der \lstinline{for}-Schleife
\lstinline{a[1]} als unbekanntes Tier (Nr.\ 4) erkennt und
\lstinline[style=terminal]{Error in animal: cow} ausgibt.
\item[(d)]
\textbf{Beschreiben Sie -- in Worten und/oder als C-Quelltext -- einen Weg,
das Programm so zu berichtigen, daß es die Eingabedaten
(``A duck has 2 wings. A cow has 4 legs.'') korrekt speichert und ausgibt.}
Damit die \emph{Vererbung\/} zwischen den Objekten
\lstinline{animal}, \lstinline{with_wings} und \lstinline{with_legs}
funktioniert, müssen die abgeleiteten Klassen \lstinline{with_wings} und \lstinline{with_legs}
alle Datenfelder der Basisklasse \lstinline{animal} erben.
In C geschieht dies explizit; die Datenfelder müssen in den
abgeleiteten Klassen neu angegeben werden
(siehe \gitfile{hp}{2020ws/20210128}{loesung-3d-1.c}):
\begin{lstlisting}[gobble=8]
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int type;
char *name;
int wings;
} with_wings;
typedef struct with_legs
{
int type;
char *name;
int legs;
} with_legs;
\end{lstlisting}
Zusätzlich ist es notwendig,
die Instanzen \lstinline{duck} und \lstinline{cow}
der abgeleiteten Klassen \lstinline{with_wings} und \lstinline{with_legs}
auch als solche zu deklarieren,
damit für sie genügend Speicher reserviert wird:
\begin{lstlisting}[gobble=8]
animal *a[2];
with_wings duck;
a[0] = (animal *) &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
((with_wings *) a[0])->wings = 2;
with_legs cow;
a[1] = (animal *) &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
((with_legs *) a[1])->legs = 4;
\end{lstlisting}
Wenn man dies vergißt und sie nur als \lstinline{animal} deklariert,
wird auch nur Speicherplatz für (kleinere)
\lstinline{animal}-Variable angelegt.
Dadurch kommt es zu Speicherzugriffen außerhalb der
deklarierten Variablen, was letztlich zu einem Absturz führt
(siehe \gitfile{hp}{2020ws/20210128}{loesung-3d-0f.c}).
Für die Zuweisung eines Zeigers auf \lstinline{duck}
an \lstinline{a[0]}, also an einen Zeiger auf \lstinline{animal}
wird eine weitere explizite Typumwandlung notwendig.
Entsprechendes gilt für die Zuweisung eines Zeigers auf \lstinline{cow}
an \lstinline{a[1]}.
Es ist sinnvoll, explizite Typumwandlungen so weit wie möglich zu vermeiden.
Es ist einfacher und gleichzeitig sicherer,
direkt in die Variablen \lstinline{duck} und \lstinline{cow}
zu schreiben, anstatt dies über die Zeiger \lstinline{a[0]}
und \lstinline{a[1]} zu tun
(siehe \gitfile{hp}{2020ws/20210128}{loesung-3d-2.c}):
\begin{lstlisting}[gobble=8]
animal *a[2];
with_wings duck;
a[0] = (animal *) &duck;
duck.type = WITH_WINGS;
duck.name = "duck";
duck.wings = 2;
with_legs cow;
a[1] = (animal *) &cow;
cow.type = WITH_LEGS;
cow.name = "cow";
cow.legs = 4;
\end{lstlisting}
\item[(e)]
\textbf{Schreiben Sie das Programm so um,
daß es keine expliziten Typumwandlungen mehr benötigt.}\par
\textbf{Hinweis: Verwenden Sie \lstinline{union}.}
Siehe \gitfile{hp}{2020ws/20210128}{loesung-3e.c}.
Diese Lösung basiert auf \gitfile{hp}{2020ws/20210128}{loesung-3d-2.c},
da diese bereits weniger explizite Typumwandlungen enthält
als \gitfile{hp}{2020ws/20210128}{loesung-3d-1.c}.
Arbeitsschritte:
\begin{itemize}
\item
Umbenennen des Basistyps \lstinline{animal} in \lstinline{base},
damit wir den Bezeichner \lstinline{animal}
für die \lstinline{union} verwenden können
\item
Schreiben einer \lstinline{union animal},
die die drei Klassen \lstinline{base},
\lstinline{with_wings} und \lstinline{with_legs}
als Datenfelder enthält
\item
Umschreiben der Initialisierungen:
Zugriff auf Datenfelder erfolgt nun durch
z.\,B.\ \lstinline{a[0]->b.name}.
Hierbei ist \lstinline{b} der Name des \lstinline{base}-Datenfelds
innerhalb der \lstinline{union animal}.
\item
Auf gleiche Weise schreiben wir die \lstinline{if}-Bedingungen
innerhalb der \lstinline{for}-Schleife
sowie die Parameter der \lstinline{printf()}-Aufrufe um.
\end{itemize}
Explizite Typumwandlungen sind nun nicht mehr nötig.
Nachteil dieser Lösung:
Jede Objekt-Variable belegt nun Speicherplatz
für die gesamte \lstinline{union animal},
anstatt nur für die benötigte Variable vom Typ
\lstinline{with_wings} oder \lstinline{with_legs}.
Dies kann zu einer Verschwendung von Speicherplatz führen,
auch wenn dies in diesem Beispielprogramm tatsächlich nicht der Fall ist.
\item[(f)]
\textbf{Schreiben Sie das Programm weiter um,
so daß es die Objektinstanzen \lstinline{duck} und \lstinline{cow}
dynamisch erzeugt.}\par
\textbf{Hinweis: Verwenden Sie \lstinline{malloc()} und schreiben Sie Konstruktoren.}
Siehe \gitfile{hp}{2020ws/20210128}{loesung-3f.c}.
\item[(g)]
\textbf{Schreiben Sie das Programm weiter um,
so daß die Ausgabe nicht mehr direkt im Hauptprogramm erfolgt,
sondern stattdessen eine virtuelle Methode \lstinline{print()}
aufgerufen wird.}\par
\textbf{Hinweis: Verwenden Sie in den Objekten Zeiger auf Funktionen,
und initialisieren Sie diese in den Konstruktoren.}
Siehe \gitfile{hp}{2020ws/20210128}{loesung-3g.c}.
\end{itemize}
\end{document}
File added
% hp-uebung-20210128.pdf - Exercises on Low-Level Programming
% Copyright (C) 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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: Speicherformate von Zahlen, Personen-Datenbank, objektorientierte Tier-Datenbank
\documentclass[a4paper]{article}
\usepackage{pgscript}
\begin{document}
% \thispagestyle{empty}
\section*{Hardwarenahe Programmierung\\
Übungsaufgaben -- 28.\ Januar 2021}
Diese Übung enthält Punkteangaben wie in einer Klausur.
Um zu "`bestehen"', müssen Sie innerhalb von 100 Minuten
unter Verwendung ausschließlich zugelassener Hilfsmittel
17 Punkte (von insgesamt \totalpoints) erreichen.
\exercise{Speicherformate von Zahlen}
Wir betrachten das folgende Programm (\gitfile{hp}{2020ws/20210128}{aufgabe-1.c}):
\begin{lstlisting}
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint32_t a;
uint64_t b;
uint8_t c;
} three_numbers;
int main (void)
{
three_numbers xyz = { 1819042120, 2410670883059281007, 0 };
printf ("%s\n", &xyz);
return 0;
}
\end{lstlisting}
Das Programm wird für einen 32-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m32} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 32-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall -m32 aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function "main":
aufgabe-2.c:14:13: warning: format "%s" expects argument of type "char *", but
argument 2 has type "three_numbers * {aka struct <anonymous> *}" [-Wformat=]
printf ("%s\n", &xyz);
^
$ ¡./aufgabe-2¿
Hallo, Welt!
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die beim Compilieren auftretende Warnung.
\points{2}
\item
Erklären Sie die Ausgabe des Programms.
\points{4}
\item
Welche Endianness hat der verwendete Rechner?
Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?
\points{2}
\item
Dasselbe Programm wird nun für einen 64-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m64} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 64-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal,gobble=8]
$ ¡gcc -Wall -m64 aufgabe-2.c -o aufgabe-2¿
aufgabe-2.c: In function "main":
aufgabe-2.c:14:13: warning: format "%s" expects argument of type "char *",
but argument 2 has type "three_numbers * {aka struct <anonymous> *}"
[-Wformat=]
printf ("%s\n", &xyz);
^
$ ¡./aufgabe-2¿
Hall5V
\end{lstlisting}
(Es ist möglich, daß die konkrete Ausgabe auf Ihrem Rechner anders aussieht.)\par
Erklären Sie die geänderte Ausgabe des Programms.
\points{3}
\end{enumerate}
\clearpage
\exercise{Personen-Datenbank}
Wir betrachten das folgende Programm (\gitfile{hp}{2020ws/20210128}{aufgabe-2.c}):
\begin{lstlisting}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strcpy (sls.first_name, "Sabine");
strcpy (sls.family_name, "Leutheusser-Schnarrenberger");
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
\end{lstlisting}
Die Standard-Funktion \lstinline{strcpy()} bewirkt ein Kopieren eines Strings
von rechts nach links, hier also z.\,B.\ die Zuweisung der String-Konstanten
\lstinline{"Sabine"} an die String-Variable \lstinline{sls.first_name[]}.
Das Programm wird für einen 32-Bit-Rechner compiliert und ausgeführt.\\
(Die \lstinline[style=cmd]{gcc}-Option \lstinline[style=cmd]{-m32} sorgt dafür,
daß \lstinline[style=cmd]{gcc} Code für einen 32-Bit-Prozessor erzeugt.)
\begin{lstlisting}[style=terminal]
$ ¡gcc -Wall -O -m32 aufgabe-2.c -o aufgabe-2¿
$ ¡./aufgabe-2¿
Sabine Leutheusser-Schnarrenberger wurde am 110.98.1701278309 geboren.
Speicherzugriffsfehler
\end{lstlisting}
\begin{enumerate}[\quad(a)]
\item
Erklären Sie die Ausgabe des Programms einschließlich der Zahlenwerte.
\points{4}
\item
Welche Endianness hat der verwendete Rechner?
Begründen Sie Ihre Antwort.
\points{1}
\item
Wie sähe die Ausgabe auf einem Rechner mit entgegengesetzter Endianness aus?
\points{2}
\item
Erklären Sie den Speicherzugriffsfehler.
(Es kann sein, daß sich der Fehler auf Ihrem Rechner nicht bemerkbar macht.
Er ist aber trotzdem vorhanden.)
\points{2}
\end{enumerate}
\clearpage
\exercise{Objektorientierte Tier-Datenbank}
Das unten dargestellte Programm (Datei: \gitfile{hp}{2020ws/20210128}{aufgabe-3a.c})
soll Daten von Tieren verwalten.
Beim Compilieren erscheinen die folgende Fehlermeldungen:
\begin{lstlisting}[style=terminal]
$ ¡gcc -std=c99 -Wall -O aufgabe-2a.c -o aufgabe-2a¿
aufgabe-2a.c: In function 'main':
aufgabe-2a.c:31: error: 'animal' has no member named 'wings'
aufgabe-2a.c:37: error: 'animal' has no member named 'legs'
\end{lstlisting}
Der Programmierer nimmt die auf der nächsten Seite in Rot dargestellten Ersetzungen vor\\
(Datei: \gitfile{hp}{2020ws/20210128}{aufgabe-3b.c}).
Daraufhin gelingt das Compilieren, und die Ausgabe des Programms lautet:
\begin{lstlisting}[style=terminal]
$ ¡gcc -std=c99 -Wall -O aufgabe-2b.c -o aufgabe-2b¿
$ ¡./aufgabe-2b¿
A duck has 2 legs.
Error in animal: cow
\end{lstlisting}
\begin{itemize}
\item[(a)]
Erklären Sie die o.\,a.\ Compiler-Fehlermeldungen.
\points{2}
\item[(b)]
Wieso verschwinden die Fehlermeldungen nach den o.\,a.\ Ersetzungen?
\points{3}
\item[(c)]
Erklären Sie die Ausgabe des Programms.
\points{5}
\item[(d)]
Beschreiben Sie -- in Worten und/oder als C-Quelltext -- einen Weg,
das Programm so zu berichtigen, daß es die Eingabedaten
(``A duck has 2 wings. A cow has 4 legs.'') korrekt speichert und ausgibt.\\
\points{4}
% \item[(e)]
% Schreiben Sie das Programm so um,
% daß es keine expliziten Typumwandlungen mehr benötigt.\par
% Hinweis: Verwenden Sie \lstinline{union}.
% \points{4}
% \item[(f)]
% Schreiben Sie das Programm weiter um,
% so daß es die Objektinstanzen \lstinline{duck} und \lstinline{cow}
% dynamisch erzeugt.\par
% Hinweis: Verwenden Sie \lstinline{malloc()} und schreiben Sie Konstruktoren.
% \points{4}
% \item[(g)]
% Schreiben Sie das Programm weiter um,
% so daß die Ausgabe nicht mehr direkt im Hauptprogramm erfolgt,
% sondern stattdessen eine virtuelle Methode \lstinline{print()}
% aufgerufen wird.\par
% Hinweis: Verwenden Sie in den Objekten Zeiger auf Funktionen,
% und initialisieren Sie diese in den Konstruktoren.
% \points{4}
\end{itemize}
\begin{minipage}[t]{0.34\textwidth}
\begin{lstlisting}[gobble=6,xleftmargin=0pt]
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int wings;
} with_wings;
typedef struct with_legs
{
int legs;
} with_legs;
\end{lstlisting}
\end{minipage}\hfill
\begin{minipage}[t]{0.65\textwidth}
\begin{lstlisting}[gobble=6,xleftmargin=0pt]
int main (void)
{
animal *a[2];
animal duck;
a[0] = &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
a[0]->wings = 2;
animal cow;
a[1] = &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
a[1]->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
\end{lstlisting}
\begin{picture}(0,0)
\color{red}
\put(3.7,6.207){\vector(-1,0){0.7}}
\put(3.8,6.207){\makebox(0,0)[l]{\lstinline[basicstyle=\color{red}]{((with_legs *) a[1])->legs = 4;}}}
\put(4.0,8.735){\vector(-1,0){0.7}}
\put(4.1,8.735){\makebox(0,0)[l]{\lstinline[basicstyle=\color{red}]{((with_wings *) a[0])->wings = 2;}}}
\end{picture}
\end{minipage}
\begin{flushright}
\textit{Viel Erfolg!}
\end{flushright}
\makeatletter
\immediate\write\@mainaux{\string\gdef\string\totalpoints{\arabic{points}}}
\makeatother
\end{document}
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint32_t a;
uint64_t b;
uint8_t c;
} three_numbers;
int main (void)
{
three_numbers xyz = { 1819042120, 2410670883059281007, 0 };
printf ("%x %x %x\n", xyz.a, xyz.b, xyz.c);
printf ("%08x %016x %02x\n", xyz.a, xyz.b, xyz.c);
printf ("%s\n", &xyz);
return 0;
}
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint32_t a;
uint64_t b;
uint8_t c;
} three_numbers;
int main (void)
{
three_numbers xyz = { 1819042120, 2410670883059281007, 0 };
printf ("%x %lx %x\n", xyz.a, xyz.b, xyz.c);
printf ("%08x %016lx %02x\n", xyz.a, xyz.b, xyz.c);
printf ("%s\n", &xyz);
return 0;
}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
strcpy (sls.first_name, "Sabine");
strcpy (sls.family_name, "Leutheusser-Schnarrenberger");
sls.day = 26;
sls.month = 7;
sls.year = 1951;
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strncpy (sls.first_name, "Sabine", 10);
strncpy (sls.family_name, "Leutheusser-Schnarrenberger", 20);
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strncpy (sls.first_name, "Sabine", 9);
strncpy (sls.family_name, "Leutheusser-Schnarrenberger", 19);
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
#include <stdio.h>
#include <string.h>
typedef struct
{
char first_name[10];
char family_name[20];
char day, month;
int year;
} person;
int main (void)
{
person sls;
sls.day = 26;
sls.month = 7;
sls.year = 1951;
strncpy (sls.first_name, "Sabine", 9);
sls.first_name[9] = 0;
strncpy (sls.family_name, "Leutheusser-Schnarrenberger", 19);
sls.family_name[19] = 0;
printf ("%s %s wurde am %d.%d.%d geboren.\n",
sls.first_name, sls.family_name, sls.day, sls.month, sls.year);
return 0;
}
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int type;
char *name;
int wings;
} with_wings;
typedef struct with_legs
{
int type;
char *name;
int legs;
} with_legs;
int main (void)
{
animal *a[2];
animal duck;
a[0] = &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
((with_wings *) a[0])->wings = 2;
animal cow;
a[1] = &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
((with_legs *) a[1])->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int type;
char *name;
int wings;
} with_wings;
typedef struct with_legs
{
int type;
char *name;
int legs;
} with_legs;
int main (void)
{
animal *a[2];
with_wings duck;
a[0] = (animal *) &duck;
a[0]->type = WITH_WINGS;
a[0]->name = "duck";
((with_wings *) a[0])->wings = 2;
with_legs cow;
a[1] = (animal *) &cow;
a[1]->type = WITH_LEGS;
a[1]->name = "cow";
((with_legs *) a[1])->legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
#include <stdio.h>
#define ANIMAL 0
#define WITH_WINGS 1
#define WITH_LEGS 2
typedef struct animal
{
int type;
char *name;
} animal;
typedef struct with_wings
{
int type;
char *name;
int wings;
} with_wings;
typedef struct with_legs
{
int type;
char *name;
int legs;
} with_legs;
int main (void)
{
animal *a[2];
with_wings duck;
a[0] = (animal *) &duck;
duck.type = WITH_WINGS;
duck.name = "duck";
duck.wings = 2;
with_legs cow;
a[1] = (animal *) &cow;
cow.type = WITH_LEGS;
cow.name = "cow";
cow.legs = 4;
for (int i = 0; i < 2; i++)
if (a[i]->type == WITH_LEGS)
printf ("A %s has %d legs.\n", a[i]->name,
((with_legs *) a[i])-> legs);
else if (a[i]->type == WITH_WINGS)
printf ("A %s has %d wings.\n", a[i]->name,
((with_wings *) a[i])-> wings);
else
printf ("Error in animal: %s\n", a[i]->name);
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment