Sie sind auf Seite 1von 9

Fortgeschrittenes Programmieren in C

SS 2020
Übungen
Modus:
In den Präsenzeinheiten werden selbständig Beispiele erstellt und Tipps und Tricks vorgezeigt
und besprochen sowie Themen aus der Vorlesung diskutiert. In der dritten (4.5.), vierten
(19.5.) und fünften (8.6.) Präsenzeinheit gibt es jeweils einen Übungstest, bei dem je maximal
10 Punkte erreichbar sind. Die besten zwei Tests werden für die Note gewertet.
In der Fernlehre sind im Laufe des Semesters insgesamt drei Übungsbeispiele auszuarbeiten
und in einem Dokument gesammelt zu dokumentieren. Dafür werden ebenfalls je Beispiel
maximal 10 Punkte vergeben. Dabei wird nicht nur die korrekte, genau der Angabe
entsprechende Funktion des Programms sowie Beachtung aller eventuell angegeben Rand-
bedingungen bewertet, sondern auch die Struktur, Übersichtlichkeit und Kommentierung des
Programms sowie die Verwendung sinnvoller Variablennamen. In der Dokumentation ist die
Grundidee der Lösung sowie gegebenenfalls der verwendeten Datenstrukturen textuell zu
erläutern, eventuell mit Struktogrammen oder Blockdiagrammen. Ebenso zu dokumentieren
ist die Teststrategie, also mit welchen Daten warum das Programm getestet wurde sowie die
Testergebnisse bzw. eventuell beim Test gefundene Fehler. Achten Sie auch auf unspecified
oder undefined behaviour Ihres Programmes. Es gibt keine Punkte für eine Aufgabe im Fall,
dass der Code zu unspecified oder undefined behaviour führen kann!
Notenskala: 47 – 50 Pkte.: Sehr Gut
41 – 46 Pkte.: Gut
35 – 40 Pkte.: Befriedigend
31 – 34 Pkte.: Genügend
< 30 Pkte.: Nicht Genügend

Außerdem muss jeder (gewertete) Übungstest positiv sein, d.h. man muss mindestens 6
Punkte erreichen.
Die Dokumentation mit den Fernlehraufgaben sind bis spätestens 28.6.2020 als Word- oder
pdf-Datei abzugeben (Moodle), die zugehörigen Quelldateien (nur .c- bzw. .h-Dateien) sind
ebenfalls hochzuladen.
Hinweise:
 Lesen Sie die Angaben vollständig und genau durch, bei Nichtbeachtung der jeweiligen
Randbedingungen in den Lösungen gibt es Punkteabzüge bis hin zu 10 Punkten!
 Die Beispiele können in C oder C++ gelöst werden. (Die Verwendung der C++ STL ist
zulässig, muss aber sinnvoll und begründet (Kommentar) sein! Außerdem dürfen selbst zu
erstellende Funktionen und Datentypen nicht durch solche aus der STL ersetzt werden.)
 Falls Angaben unvollständig erscheinen, treffen Sie eigene sinnvolle Annahmen, um das
Beispiel zu lösen! Diese sind in Dokumentation und Kommentaren im Code festzuhalten.
 Verwenden Sie korrekte und sinnvolle (!) Datentypen (wo nicht explizit vorgegeben).
Faustregel: So groß wie nötig, so klein wie möglich.

FH-Prof. Dipl.-Ing. Herbert Paulis Eveline Prochaska, BSc, MSc


herbert.paulis@fh-campuswien.ac.at eveline.prochaska@fh-campuswien.ac.at
Inhalte:

Die folgenden Themenkomplexe sollen im Rahmen der Übung beherrscht und verfestigt
werden:

 Bitoperationen
 Pointer
o Operationen über Pointer
o Dynamische Speicherallokation
o Pointer mit Datenstrukturen
 Arrays
 Strukturen
 Verkettete Listen
o Pointer auf Pointer
 SW-Problemlösungskompetenz
o Analyse von Anforderungen
 Erkennen fehlender Anforderungen  Treffen von Annahmen
o Entwurf von Daten- und Programmstrukturen
o Erstellung einer Teststrategie
 Sinnvolle Testdaten zur möglichst vollständigen Abdeckung des Lösungsraums
 Testdurchführung und Testdokumentation
 Einhalten von Programmierkonventionen
o Erstellen von übersichtlichem und klaren Code
o Dokumentation
 Error Handling
o Gängige Programmierfehler erkennen und vermeiden
o Fehlerlokalisation in Programmen
o Verständnis von Fehlermeldungen des Compilers und des Linkers
o Erkennen und Vermeiden von unerwünschtem Verhalten eines Programmes
 unspecified und undefined behaviour

Bitoperationen

Überprüfen Sie bei den folgenden Aufgaben dieses Kapitels Ihre Ergebnisse in einem
geeigneten Hauptprogramm mit mehreren Testdaten (Randbedingungen!) und verwenden
Sie dazu die folgende Funktion, wobei Sie für XXX den jeweils benötigten Datentyp für
den Funktionsparameter einsetzen:

// n als Bitmuster ausgeben:


void printBits(const XXX n)
{
for (int i = 8 * sizeof(n) - 1; i >= 0; i--)
{
printf("%d", (n >> i & 1)); // i-tes Bit ausgeben
if (i % 4 == 0 && i > 0) // und nach 4 Bits
printf(" "); // eine Leerstelle.
}
printf("\n");
}

Programm 1: Schreiben Sie ein Programm, das …

a) … in einer Funktion swapBytes(…) in einem 16 Bit-Wort das High- und das Low-
Byte tauscht. Die Funktion erhält als Argument einen Wert vom Typ unsigned
short und liefert das Ergebnis als Return-Wert.

b) … mit einer Funktion getBitfield(…) den Wert einer Bitgruppe zurückgibt. Die
Funktion erhält drei Argumente: 1) ein Wert vom Typ unsigned long, der die
Bitgruppe enthält, 2) das Startbit und 3) die Länge des Bitfeldes. Bei ungültigen
Werten für 2) und/oder 3) soll eine globale Variable int BitFieldError auf -1
gesetzt werden, wenn kein Fehler aufgetreten ist, auf 0.

Beispiel: mit a = 0x0f0f0f0f (≙ 0000 1111 0000 1111 0000 1111 0000 1111B) und
dem Aufruf getBitfield(a, 11, 8) soll der Wert 225 (≙ 0xe1 oder 1110
0001B) zurückgegeben werden.

c) … mit einer Funktion setBitfield(…) den Wert einer Bitgruppe neu setzt. Die
Parameter sind analog zu Aufgabe b) mit einem zusätzlichen vierten Parameter, der
den neuen Wert der Bitguppe enthält. Rückgabewert ist das Argument 1 mit den neu
gesetzten Bits, die Fehlerbehandlung erfolgt analog zu Aufgabe b).
Programm 2: Bits im Kreis rotieren – Fernlehraufgabe 1

Entwerfen Sie eine Funktion rotateBits(…), die einen unsigned long-Wert um eine
bestimmte Anzahl Bits verschiebt, aber dabei das Bitmuster als Ring ansieht. Es sollen also
im Gegensatz zu den Operatoren >> und << keine Nullen nachgeschoben werden, sondern
die auf der anderen Seite hinausgeschobenen Bits. Das erste Argument soll den Wert
enthalten, dessen Bits rotiert werden, das zweite Argument die Anzahl der Bits, um die rotiert
werden soll. Ist das zweite Argument > 0, wird nach links rotiert, bei < 0 nach rechts.
Rückgabewert ist der Wert des ersten Arguments nach dem Rotieren.

Beispiel:
rotateBits(0x12345678, 8) liefert 0x34567812 als Ergebnis,
rotateBits(0x12345678, -8) liefert 0x78123456

Erstellen Sie unter Zuhilfenahme der vorgegebenen Ausgabefunktion aus Beispiel 1 ein
Testprogramm und testen Sie Ihre Funktion unter besonderer Berücksichtigung aller
Sonderfälle.
Fehler und Fehlersuche bzw. -vermeidung

Programm 3: sizeof()

Welche Größe hat folgendes Objekt?

char str[] = "a short string";

Würde sich etwas an der Größe ändern, wenn man char[] durch char* ersetzte?
Begründen Sie Ihre Antwort.

Programm 4: Suspekte for-Schleife

Terminiert das folgenden Programm?

#include <stdio.h>

int main(void)
{
int i;
int a[8];
for (i = 0; i <= sizeof(a); i++)
{
printf("%d\n", i);
a[i] = 0;
}
return 0;
}

a) Übersetzen Sie das Programm mit der Einstellung „Keine Optimierung“ und starten
Sie es. Überlegen Sie, warum sich dieses Programm so verhält und wie man es
richtigstellen könnte.
b) Macht es auf Ihrem Rechner einen Unterschied, ob Sie das Programm mit oder ohne
Optimierung durch den Compiler ausführen? Warum, bzw. warum nicht?
c) Was für ein potentiell gefährlicher Fehler versteckt sich noch in diesem Programm?
Programm 5: Code-Layout

In der Funktion sum() hat sich eine for-Schleife versteckt. Schreiben Sie die Funktion so
um, dass sie übersichtlicher ist. Geben Sie den Variablen bei dieser Gelegenheit
selbsterklärende Namen. Testen Sie die neue Funktion in einem geeigneten Hauptprogramm
mit einem Array mit zufälligen Werten.

#define susi josi

/**
* Berechnet die Summe der Zahlen im übergebenen array
* @breite Zahlen
* @hoehe länge
*/
int sum(int *breiTee, int hoeHe)
{
int vorgabe = 0;
int i = 1;
do
{
int susi = vorgabe + breiTee[i-1];
vorgabe = josi;
i += 1;
} while (i <= hoeHe);

return vorgabe;
}
Pointer

Programm 6: Pointer-Knobelei

Geben Sie das untenstehende Programm ein und lassen Sie es laufen. Sobald Sie sich von der
Überraschung erholt haben, analysieren Sie das Programm Zeile für Zeile und versuchen Sie,
zu verstehen, warum es das macht, was es macht. 

#include <stdio.h>

int main()
{
char zeile[] = "Ist das ein Stuss!";
char* p = zeile;
while (*p++ != 'S');
--------*p; // 8x "-"
*++p = *(zeile + 5);
printf("%s\n", zeile);;
}

Programm 7: Arrays als Funktionsparameter

Schreiben Sie ein Programm, das per Eingabe die Größe eines Arrays für integer-Werte
übergeben bekommt. Das Programm soll anschließend Speicher auf dem Heap allokieren und
diesen als Array verwenden, um vom Benutzer einzugebende integer -Werte darin zu
speichern. In diesem Array soll anschließend der größte Wert gefunden und angezeigt
werden.

Vorgehensweise:

(1) Lesen Sie die gewünschte Arraygröße von der Standardeingabe und allokieren Sie
einen entsprechend großen Speicherblock.
(2) Achten Sie dabei auf Fehlerbehandlung bei ungültigen Angaben.
(3) Lesen Sie n Integer-Werte von der Standardeingabe und speichern Sie diese im vorher
allokierten Array.
(4) Übergeben Sie das Array an eine Funktion find_max, die den größten Wert
zurückgeben soll.
(5) Implementieren Sie die Funktion find_max zur Suche nach dem maximalen Wert
unter Verwendung von Pointerarithmetik (also keine direkte Indexadressierung bei
Vergleichen).

Achtung: Sinn der Aufgabe ist das Verstehen des Zusammenhangs zwischen Pointern und
Arrays, deshalb sollen Sie das Array als Pointer übergeben. Das ist besonders wichtig, wenn
Sie Werte in einem Array modifizieren oder ein Array zurückgeben wollten (das geht in C
nicht!).
Programm 8: Pointer Swapping – Fernlehraufgabe 2

(1) Schreiben Sie eine Funktion swap, die zwei Integer vertauscht.
Hinweis: Sie müssen Zeiger auf die int-Variablen übergeben.
Definieren Sie zwei int -Variablen, geben Sie diese aus, vertauschen Sie sie und geben
Sie sie danach erneut aus.
(2) Schreiben Sie nun eine Funktion, die zwei int -Pointer vertauscht. Benutzen Sie dabei
nicht malloc, sondern definieren sie zwei int -Variablen i und j und speichern Sie
deren Adressen in zwei Pointer-Variablen p1 und p2.
Geben Sie vor und nach dem Tauschen p1 und p2 aus, dereferenzieren Sie p1 und p2
und geben Sie das Ergebnis ebenfalls vor und nach dem Vertauschen aus. Geben Sie
außerdem i und j aus. Welche Ergebnisse erwarten Sie?
(3) Machen Sie die swap-Funktion aus (2) generisch, so dass sie für beliebige Typen von
Pointern funktioniert. (Hinweis: Benutzen Sie hierzu void-Pointer. Vergessen Sie das
Casten beim Aufruf nicht!)
Testen Sie die Funktion zunächst mit int-, dann mit float-Pointern.

Programm 9: Function Pointer

(4) Definieren Sie Funktion sum, dif, prod, quot, y_log_x und x_pwr_y, die
jeweils zwei float-Parameter besitzen und als Ergebnis diese zwei Parameter jeweils
gemäß ihrem Namen verknüpft als float-Wert zurückliefern. Achten Sie dabei auf
sinnvolle Fehlerbehandlung bei unzulässigen Werten. Weisen Sie diese Funktionen
einem Array math von passend definierten Function Pointern zu und rufen Sie die
Funktionen nacheinander in einer Schleife mit einer einzigen Anweisung auf. Testen Sie
Ihr Programm mit geeigneten Daten inkl. Randbedingungen und fehlerhaften Werten.

Hinweis: logy (x) = log10 (x) / log10 (y)

(5) Vereinfachen Sie die Funktionsdefinition der Funktion signal():

void (*signal(int, void(*)(int)))(int);

mit Hilfe von typedef so, dass sie übersichtlicher und leichter verständlich wird.
Programm 10: Eine universelle Sortierfunktion – Fernlehraufgabe 3

Schreiben Sie eine Funktion bubbleSort(…), die ein Array mit Elementen beliebiger
Datenstrukturen (das kann z.B. auch eine beliebige struct sein!) nach einer beliebigen
Relation R sortieren kann. Die Funktion bekommt einen Pointer data auf das erste Element
des zu sortierenden Arrays übergeben, die Größe eines Elementes von data in Bytes sowie
einen Funktionszeiger auf eine Vergleichsfunktion. Diese hat ihrerseits als Argumente zwei
Pointer p1 und p2 auf Elemente vom Typ der Datenstruktur des zu sortierenden Arrays und
liefert den int-Wert 1 zurück, wenn p1 in Relation zu p2 steht und 0 sonst. Am Ende der
Funktion sollen die Daten in dem Array data in der Reihenfolge der verlangten Relation
stehen, d.h. es muss für alle Elemente des Arrays gelten: data[i] R data[i+1].

Testen Sie Ihre Funktion in einem einzigen (!) Programm mit einem jeweils 12-elementigen
Array der folgenden Datenstruktur sowie der angegebenen Relation:

Datenstruktur Relation R

unsigned >

float <=

struct widerstand { Widerstand ist >


int strom;
int spannung;
}

Zum Testen der Funktion müssen Sie die jeweiligen Datenstrukturen anlegen und auch die
erforderlichen Vergleichsfunktionen für die benötigten Relationen definieren. Weisen Sie den
Elementen der Arrays zufällige Werte zu und geben Sie sie vor und nach dem Sortieren aus.

Hinweise:
 Für die Funktion des Verfahrens Bubblesort siehe:
https://de.wikipedia.org/wiki/Bubblesort
 Sie werden auch eine entsprechende Funktion swap(…) benötigen, die zwei der zu
sortierenden Elemente vertauscht. Überlegen Sie selbst, welche Parameter diese Funktion
benötigt. Siehe dazu auch Programm 8.