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

Notizen Klausur-Fragestunde 30.1.2025

parent 116c28c9
No related branches found
No related tags found
No related merge requests found
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = pos; i < len; i++)
{
target[i+1] = target[i];
printf ("i = %d, target = \"%s\"\n", i, target);
}
target[pos] = src;
printf ("src eingefügt, target = \"%s\"\n", target);
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
Themen, 30.01.2025, 11:19:15
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Seiteneffekte
- Zeiger, Strings, Arrays
- Hardwarenahe Programmierung: Bit-Manipulation
- Hardwarenahe Programmierung: Daten im Speicher:
Aufteilung auf Speicherzellen, Endianness, Alignment
- Objektorientierte Programmierung, Zeiger auf Funktionen
- Algorithmen, Landau-Symbole
Beispiele für Seiteneffekte
- Aufgabe 6.1: s[i++]
Beispiele für Zeiger, Strings, Arrays:
- Aufgabe 2.3, 3.1, 3.2, 6.1, 11.4
Beispiele für Bit-Manipulation:
- Klausur von 2016, Aufgabe 4: XBM-Dateien
- Aufgaben zu Mikrocontrollern: 5.2, 5.3, 9.1
- Aufgabe 8.2, 11.3
Beispiele für Daten im Speicher:
- Aufgabe 8.1, 9.3, 10.1
Beispiele für objektorientierte Programmierung:
- Callbacks: Aufgabe 6.2
- Aufgabe 10.3
- Klausur von 2016, Aufgabe 3
Beispiele für Algorithmen, Landau-Symbole:
- Aufgabe 9.2, 10.2
- Klausur von 2016, Aufgabe 1(d)
Nicht relevant:
- Aufgabe 5.1 (Nicht als Aufgabe. Daß Sie das können, setze ich voraus.;-)
- };->== (Gnu)
- verkettete Listen, Bäume
- Rechnertechnik: Assembler, Redcode usw.
- bestenfalls rudimentär: make, Präprozessor
Probeklausur vom 29.1.2016, 30.01.2025, 11:46:51
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Aufgabe 1:
(a)
- *Nicht* den Quelltext Zeile für Zeile beschreiben.
- Dies ist nicht der Bubble-Sort, sondern der Selection-Sort.
Vorsicht: Nicht jeder Quelltext, der so aussieht, ist
überhaupt ein funktionierender Sortier-Algorithmus!
- Beispiel für eine Erklärung der Funktion:
Die Funktion geht in einer äußeren Schleife einen String durch.
Für jeden Buchstaben im String geht die Funktion in einer
inneren Schleife alle rechts davon stehenden Zeichen durch
und sucht nach einem, das kleiner ist. In diesem Fall tauscht
es das kleinere Zeichen nach vorne, also an die Stelle, an der
wir gerade mit der äußeren Schleife sind. Nachdem die äußere
Schleife durchgelaufen ist, ist der String gemäß der
ASCII-Reihenfolge sortiert (Selection-Sort).
Dies ist in Eile ("unter Klausur-Bedingungen") heruntergeschrieben.
Mit mehr Zeit könnte man das natürlich besser und kürzer formulieren.
Hierfür gäbe es aber volle Punktzahl.
(b) ASCII-Reihenfolge: Groß- vor Kleinbuchstaben
(c) Der String hat dann keinen Null-Terminator.
printf() wird dann über den String hinaus aus dem Speicher lesen.
Dies kann zu einem Absturz führen.
(d) O(n²), weil zwei geschachtelte Schleifen
(e) („Quicksort“ log n)
In einem alphabetisch sortierten Array kann man die Suche in der Mitte
beginnen und sich durch Halbieren der Intervalle an die gesuchte Position
herantasten. Wegen des fortwährenden Halbierens geschieht dies in O(log n).
--> Damit verkürzt sich die innere Schleife von O(n) auf O(log n).
Zusammen mit der äußeren Schleife haben wir dann O(n log n).
Zusammen mit Rekursion ergibt dies den Merge-Sort.
Alternative: In der inneren Schleife feststellen, ob das Array vielleicht
bereits sortiert ist. Dies führt auf den Bubble-Sort.
Alternative: Das Wort "Quicksort" hinschreiben. --> Teilpunkte
Alternative: Das Wort "Quicksort" hinschreiben und erklären. --> volle Punktzahl
Alternative: Quicksort implementieren --> volle Punktzahl
(z.B.: Beispiel-Programm qsort-3.c so modifizieren, daß es die Buchstaben
in einem String sortiert anstelle eines Arrays von Strings.)
Aufgabe 2:
(a) Funktion output() erklären
Die Funktion gibt die in dem Array enthaltenen Integer-Werte aus.
Das Array wird übergeben als ein Zeiger auf vorzeichenlose 16-Bit-Variablen.
Das Ende des Arrays wird durch den Zahlenwert 0 erkannt.
(b) Begründen Sie den Unterschied zwischen der ersten (2 3 5 7 11 13 17)
und der zweiten Zeile (3 5 7 11 13 17) der Ausgabe des Programms. (2 Punkte)
Ein Pointer auf das Array zeigt immer auf das erste Zeichen im Array.
Wenn man den Pointer um 1 erhöht, zeigt er auf das zweite Zeichen,
(c) Erklären Sie die beim Compilieren auftretenden Warnungen
und die dritte Zeile (2 3 5 7 11 13 17) der Ausgabe des Programms. (3 Punkte)
Die Funktion erwartet einen Zeiger auf uint16_t,
bekommt aber einen Zeiger auf char.
--> Warnungen erklärt.
Der Zeiger zeigt aber auf die korrekte Speicherzelle,
daher funktioniert die Ausgabe des Arrays normal.
(d) Erklären Sie die vierte Zeile (768 1280 1792 2816 3328 4352) der Ausgabe des Programms.
Sie dürfen einen Little-Endian-Rechner voraussetzen. (4 Punkte)
zeigt auf speicheradresse 0x01 also nur 1 byte verschoben
Bildchen malen: Bytes im Speicher
16-Bit-Zahlen: | 2 | 3 | 5 | 7 | 11 | 13 | 17 | 0 |
8-Bit-Speicherzellen: | 2 | 0 | 3 | 0 | 5 | 0 | 7 | 0 | 11 | 0 | 13 | 0 | 17 | 0 | 0 | 0 |
^
Hier setze ich Little-Endian voraus. Bei Big-Endian wäre es: | 0 | 2 | 0 | 3 | ...
Zeiger p2 zeigt zunächst auf die erste Speicherzelle (mit der 2).
| 2 | 0 | 3 | 0 | 5 | 0 | 7 | 0 | 11 | 0 | 13 | 0 | 17 | 0 | 0 | 0 |
^
p2
Nach dem p2++ zeigt p2 auf die zweite Speicherzelle (mit einer 0).
| 2 | 0 | 3 | 0 | 5 | 0 | 7 | 0 | 11 | 0 | 13 | 0 | 17 | 0 | 0 | 0 |
^
p2
Nun geben wir die dort befindliche 16-Bit-Zahl aus: | 0 | 3 |
hexadezimal: | 0x00 | 0x03 |
In Little-Endian ist dies: 0x0300, also 768.
(In Big-Endian wäre dies: 0x0003, also 3.)
Und so weiter.
char = 8-Bit-Zahl, z.B.: | 2 | 1 Speicherzelle
uint16_t = 16-Bit-Zahl, z.B.: | 2 | 0 | (Little-Endian) 2 Speicherzellen
bzw. | 0 | 2 | (Big-Endian)
Typischerweise:
char = int8_t, von -128 bis +127
unsigned char = uint8_t, von 0 bis 255
int = int32_t
unsigned = uint32_t
long = int64_t auf 64-Bit-Rechner, int32_t auf 32-Bit-Rechner
unsigned long = uint64_t bzw. uint32_t
long long = int64_t
unsigned long long = uint64_t
würde die richtige ausgabe folgen wenn man 2 mal p2++ rechnen würde
--> Dann hätten wir wieder die Situation aus der zweiten Zeile.
Dann würde p2 auf die Speicherzelle mit der 3 zeigen,
und output() würde das Array ab der 3 ausgeben.
Aufgabe 3:
(a) Zeiger auf Funktion, die ein int zurückliefert
und zwei int-Parameter a und b erwartet.
Zweck:
- erzeugt einen dynamischen aufruf von funktionen
- virtuelle Methode
? "Callback"
- In jedem operation-Objekt ist gespeichert,
welche Methode aufgerufen werden soll,
um das Rechenergebnis der Operation wirklich auszurechnen.
(Der vierte Zeiger im Array ist auf NULL gesetzt,
um das Ende des Arrays zu kennzeichnen.)
Hinter der Deklaration des operation-Objekts dürfen weitere
Klassen-Deklarationen folgen, weitere typedefs.
Insofern könnte man auch hier ein "[...]" ergänzen.
(b) Konstruktoren und Methoden schreiben.
--> siehe: loesung-3b.c
Bei den Konstruktoren verwendet man sinnvollerweise malloc(),
siehe dazu: 20250109/objects-08.c und Nachfolger.
Besonders ordentlich ist es, den Speicher nach Gebrauch
wieder freizugeben. Bei Programmen, die längere Zeit laufen
sollen, ist dies sogar absolut notwendig.
(c) Dann fehlt die Ende-Kennung des Arrays,
und die Schleife liest über das Array hinaus.
Sofern dahinter nicht ein Null-Zeiger liegt (auf 64-Bit-Rechner:
8 aufeinanderfolgende 8-Bit-Speicherzellen mit dem Wert 0),
führt dies mit hoher Wahrscheinlichkeit zu einem Speicherzugriffsfehler,
da der Struktur ein Zeiger auf eine Funktion entnommen wird,
die wir anschließend aufrufen.
kürzer: Dies führt sehr wahrscheinlich zu einem Absturz.
Aufgabe 4:
Siehe loesung-4.c.
Weitere Lösungen durch Sprachmodelle: Siehe loesung-4-*.c.
Zu loesung-4.c:
int bytes = (aufgabe_4_width + 7) / 8;
bedeutet: auf ganze Bytes auffüllen
durch 8 teilen, immer aufrunden. (Nur "/" rundet immer ab.)
Die Bedingung
if (byte & (1 << (x % 8)))
bedeutet: Ist Bit Nr. (x % 8) in dem Byte gesetzt?
Das "&" steht für eine bitweise Und-Operation:
byte = 0x14 = 0b00010100
bitweise Und-Verknüpfung mit: 1 << 2 = 0b100 = 0b00000100
Ergebnis: 0b00010100
& 0b00000100
----------
0b00000100
Dies ist != 0, also gehen wir in das "if" hinein.
Das Pixel ist gesetzt; wir geben einen Stern aus.
(Es ist "<<" wegen "LSB first". Bei "MSB first" wäre es: "0x80 >> (x % 8)".)
#include <stdio.h>
#include <string.h>
void insert_into_string (char src, char *target, int pos)
{
int len = strlen (target);
for (int i = len - 1; i >= pos; i--)
{
target[i+1] = target[i];
printf ("i = %d, target = \"%s\"\n", i, target);
}
target[pos] = src;
printf ("src eingefügt, target = \"%s\"\n", target);
}
int main (void)
{
char test[100] = "Hochshule Bochum";
insert_into_string ('c', test, 5);
printf ("%s\n", test);
return 0;
}
loesung-4-deepseek-r1-14b-qwen-distill-q4_k_m-en-pg.c
\ No newline at end of file
#include <stdio.h>
#include <stdint.h>
#include "aufgabe-4.xbm"
int main (void)
{
int bytes = (aufgabe_4_width + 7) / 8;
for (int y = 0; y < aufgabe_4_height; y++)
{
for (int x = 0; x < aufgabe_4_width; x++)
{
uint8_t byte = aufgabe_4_bits[y * bytes + x / 8];
if (byte & (1 << (x % 8)))
printf (" *");
else
printf (" ");
}
printf ("\n");
}
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment