# Wintersemester 2025/26

Quelle: https://inf.zone/extras/altklausuren/ws-2526/

**Wintersemester 2025/26.** 9 ECTS: alle Aufgaben, 180 Minuten, 120 Punkte. 6 ECTS: nur A1 bis A6, 120 Minuten, 80 Punkte.

**Altklausur**

Die Aufgaben einer früheren E-Prüfung, ohne Lösungen. Geprüft wurde am Laptop: Wo hier ein leerer Kasten steht, war in der Prüfung ein Eingabefeld. Zu jeder Teilaufgabe gab es außerdem das Kästchen [„Weiß ich nicht“](https://inf.zone/extras/exam-hints/#weiß-ich-nicht-kästchen).

Den Code, der in der Prüfung im Ordner `exam-files` lag, gibt es als [ws-2526.zip](https://inf.zone/download/extras/altklausuren/ws-2526.zip). Im Codespace laden und entpacken Sie ihn so:

```bash
wget https://inf.zone/download/extras/altklausuren/ws-2526.zip
unzip ws-2526.zip
```

Die Tests, die in der Prüfung mit `check50 checks/...` liefen, liegen nicht bei.

`file.mp3` ist ein Platzhalter: In der Prüfung war es der Ausschnitt eines Songs mit dessen Album-Cover, hier sind es drei Töne, ein selbst erstelltes Cover und andere Angaben im ID3-Tag. Aufbau, Jahr und Kommentar im ID3-Tag und der Offset aus A4b (42) sind dieselben wie in der Prüfung.

## A1: Grundlagen (17 Punkte)

### A1a (3 Punkte)

In der Programmierung und im Umgang mit Computersystemen begegnen Sie verschiedenen Dateiformaten, Zahlensystemen und Werkzeugen.

Wie weit können Sie mit 10 Wassergläsern (voll oder leer) maximal zählen, wenn Sie das Binärsystem verwenden und die 0 darstellbar sein soll?

*[Antwortfeld]*

Nennen Sie drei gängige Dateierweiterungen für Bilddateien (durch Leerzeichen getrennt):

*[Antwortfeld]*

Sie haben in C ein Taschenrechner-Programm („calc.c“) geschrieben und in der Ihnen bekannten CS50-Umgebung mit `make` kompiliert. Es gab keine Fehler. Sie führen das Programm durch Eingabe von `calc.c` im Terminal aus. Es wird ein Fehler ausgegeben. Was müssen Sie im Terminal stattdessen eingeben, um das Programm auszuführen?

*[Antwortfeld]*

### A1b (4 Punkte)

Identifizieren Sie in folgendem C-Programmcode jeweils das am besten passende Beispiel für jeden der aufgelisteten Begriffe. Geben Sie das exakte Zeichen oder die Zeichenfolge aus dem Code an, nicht die Zeilennummer. Falls für einen Begriff kein Beispiel im Programm vorhanden ist, tragen Sie *FEHLT* ein.

```c
#include <stdio.h>

int main(void)
{
    int anzahl = 10;
    float preis = 12.99;
    char kategorie = 'A';

    printf("Artikel: %d Stueck, Preis: %.2f Euro, Kategorie: %c\n",
           anzahl, preis, kategorie);

    return 0;
}
```

Ein Datentyp für ganze Zahlen:

*[Antwortfeld]*

Ein Fließkomma-Literal:

*[Antwortfeld]*

Ein Formatcode für Fließkommazahlen:

*[Antwortfeld]*

Ein String-Literal:

*[Antwortfeld]*

### A1c (2 Punkte)

Betrachten Sie folgenden C-Codeausschnitt:

```c
int n = 4;
int x = 1;
do
{
    x += x;
    n--;
}
while (n > 0);
```

Welchen Wert hat `x` nach Ausführung des Codes?

*[Antwortfeld]*

Welchen Wert hat `n` nach Ausführung des Codes?

*[Antwortfeld]*

### A1d (4 Punkte)

Nennen Sie die Linux-Kommandozeilenbefehle für folgende Aufgaben. Geben Sie nur den Befehlsnamen an (ohne Parameter).

Eine Datei von einem Webserver herunterladen:

*[Antwortfeld]*

Eine Datei in ein anderes Verzeichnis verschieben:

*[Antwortfeld]*

Den Pfad des aktuellen Verzeichnisses anzeigen:

*[Antwortfeld]*

Aufeinanderfolgende gleiche Zeilen in einer sortierten Datei entfernen:

*[Antwortfeld]*

### A1e (4 Punkte)

Analysieren Sie folgendes C-Programm:

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    FILE *datei = fopen("personen.dat", "r");
    if (datei == NULL)
    {
        return 1;
    }

    char zeile[256];

    fgets(zeile, 256, datei);
    zeile[strlen(zeile) - 1] = '\0';

    int k = 0;
    while (zeile[k] != ',' && zeile[k] != '\0')
    {
        k++;
    }
    zeile[k] = '\0';

    if (strcmp(zeile, "Name") != 0 || strcmp(&zeile[k + 1], "Info") != 0)
    {
        fclose(datei);
        return 1;
    }

    while (fgets(zeile, 256, datei) != NULL)
    {
        k = 0;
        while (zeile[k] != ',' && zeile[k] != '\0')
        {
            k++;
        }
        zeile[k] = '\0';
        int info = atoi(&zeile[k + 1]);

        if (info > 0)
        {
            printf("%s: %d\n", zeile, info);
        }
    }

    fclose(datei);
    return 0;
}
```

Geben Sie den Inhalt einer gültigen Datei *personen.dat* mit genau 2 Zeilen an, sodass das Programm exakt folgende Ausgabe erzeugt: "**Mars: 1991**" (ohne Anführungszeichen):

*[Antwortfeld, mehrzeilig]*

Wie heißt das Dateiformat, das dieses Programm einliest? (Abkürzung)

*[Antwortfeld]*

## A2: Grundlagen der C-Programmierung (14 Punkte)

### A2a (2 Punkte)

Analysieren Sie folgende Situation: Ein Programm verwendet eine globale Variable namens *counter* mit dem Startwert 50. Es gibt eine Funktion *berechnung*, die zwei Parameter *a* und *b* entgegennimmt.

Die Funktion *berechnung* enthält folgende Anweisungen:

1. Erhöht die Variable *counter* um 1.
2. Legt eine Variable *sum* auf dem Stack an.
3. Weist *sum* den Wert `a + b + counter` zu.
4. Gibt *sum* zurück.

Die Hauptfunktion des Programms arbeitet wie folgt (Pseudocode):

```text
HAUPTFUNKTION:
    x = 8
    y = 12
    ergebnis = AUFRUF berechnung(x, y)
    AUSGABE "Ergebnis: " + ergebnis
    AUSGABE "Counter: " + counter
```

Welchen Wert hat die Variable *ergebnis* nach dem Funktionsaufruf?

*[Antwortfeld]*

Welchen Wert hat *counter* nach dem Funktionsaufruf?

*[Antwortfeld]*

### A2b (2 Punkte)

Beantworten Sie folgende Fragen zu C-Bibliotheksfunktionen:

Welche C-Funktion ermittelt die Länge eines Strings? (Nur Funktionsname ohne Klammern)

*[Antwortfeld]*

Mit welcher Funktion können Sie überprüfen, ob zwei Strings identisch sind? (Nur Funktionsname)

*[Antwortfeld]*

### A2c (3 Punkte)

Das folgende Programm soll die Summe aller Zahlen von 1 bis *n* (einschließlich) berechnen, enthält aber drei Fehler:

```c
#include <cs50.h>

int calculate_sum(int n)
{
    int sum = 0;
    for (int i = 1; i < n; i++)
    {
        sum += i;
    }
    return sum;
}

int main(void)
{
    int number = get_int("Geben Sie eine Zahl ein: ");
    int result = calc_sum(number);
    printf("Die Summe von 1 bis %d ist %d\n"; number, result);
    return 0;
}
```

Finden Sie den *logischen* Fehler und geben Sie an, was geändert werden muss, um den Fehler zu beheben.

*[Antwortfeld, mehrzeilig]*

Welche Ausgabe erzeugt das korrigierte Programm für die Eingabe 511? Geben Sie nur den Wert an, den *result* enthält.

*[Antwortfeld]*

### A2d (7 Punkte)

Das Programm `statistik.c` verarbeitet ein Integer-Array `zahlen` mit den Werten und gibt Minimum, Maximum und Durchschnitt aus.

**Erwartete Ausgabe:**

```text
Min: 3
Max: 31
Avg: 13.75
```

Unten stehen 15 nummerierte Codezeilen. Elf davon ergeben zusammen das korrekte Programm – die restlichen vier gehören nicht dazu. Geben Sie die Nummern der korrekten Zeilen in der richtigen Reihenfolge an.

```c
 1: sum += zahlen[i]; }
 2: printf("Min: %d\n", min);
 3: int min = zahlen[0], max = zahlen[0]; float sum = 0;
 4: for (int i = 1; i <= 8; i++) {
 5: int main(void) {
 6: printf("Avg: %.2f\n", sum / 8);
 7: for (int i = 0; i < 8; i++) {
 8: return 0; }
 9: printf("Avg: %d\n", sum / 8);
10: #include <stdio.h>
11: if (zahlen[i] < min) min = zahlen[i]; if (zahlen[i] > max) max = zahlen[i];
12: int zahlen[] = {14, 7, 23, 3, 18, 9, 31, 5};
13: printf("Max: %d\n", max);
14: int sum = 0; int min = 0, max = 0;
15: int *zahlen; zahlen = [14, 7, 23, 3, 18, 9, 31, 5];
```

Reihenfolge der korrekten Zeilen (kommasepariert mit Leerzeichen, z.B. 5, 12, 10, ...):

*[Antwortfeld]*

## A3: IBAN validieren (15 Punkte)

Beim Bezahlen Ihrer Semestergebühren haben Sie eine sogenannte IBAN (International Bank Account Number) verwendet. Dabei handelt es sich um eine standardisierte Notation für Kontonummern.

Diese ist für Deutsche Konten folgendermaßen aufgebaut:

- 2-stelliger Ländercode bestehend aus Großbuchstaben (in Deutschland "DE")
- 2-stellige Prüfziffer
- Eine 18 stellige Kontoidentifikation, bestehend aus Bankleitzahl und Kontonummer

Ihre Aufgabe ist es nun, ein Programm zu schreiben, das eine solche IBAN als Kommandozeilenparameter entgegennimmt und auf Gültigkeit prüft. Um die Komplexität der Aufgabe überschaubar zu halten, berücksichtigen wir ausschließlich deutsche Kontonummern.

Den unvollständigen Quellcode des Programms finden Sie im Verzeichnis **exam-files/iban** in Ihrem Home-Verzeichnis. Für diese Aufgabe steht Ihnen *check50* zur Verfügung. Um *check50* zu verwenden, führen Sie folgenden Befehl in dem Verzeichnis aus, in dem sich die Datei *iban.c* befindet:

**check50 checks/iban**

Zuvor sollten Sie Ihr Programm kompilieren.

### A3a (5 Punkte)

Prüfen Sie zunächst die Eingaben des Nutzers auf Vollständigkeit:

- Gibt der Nutzer keinen oder mehr als einen Kommandozeilenparameter an, geben Sie die Nachricht: `Usage: ./iban <iban>` aus.
- Wenn die IBAN zu kurz oder zu lang ist, geben Sie die Nachricht: `IBAN too long or too short` aus.

Fügen Sie jeweils ein *Newline*-Escapezeichen hinzu und beenden Sie das Programm mit dem Rückgabewert 1.

Kopieren Sie den gesamten Quellcode von **iban.c** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

### A3b (5 Punkte)

Prüfen Sie nun die Syntax der eingegebenen IBAN.

- Wenn die ersten 2 Stellen nicht dem Ländercode `DE` entsprechen, geben Sie die Nachricht: `Not a german IBAN` aus.
- Wenn die IBAN ab der dritten Stelle (index 2) andere Zeichen als Ziffern (`0-9`) enthält, geben Sie die Nachricht: `IBAN contains invalid digits` aus.

Fügen Sie jeweils ein *Newline*-Escapezeichen hinzu und beenden Sie das Programm mit dem Rückgabewert 1.

Kopieren Sie den gesamten Quellcode von **iban.c** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

### A3c (5 Punkte)

Ermitteln Sie nun im letzten Schritt, ob die eingegebene IBAN gültig ist. Vervollständigen Sie dazu die Funktion `validate_iban`. Diese nimmt eine IBAN als String entgegen, berechnet die Prüfsumme und gibt `1` im Fall einer gültigen IBAN und `0` im Fall einer ungültigen IBAN zurück.

Geben Sie anschließend im Fall einer gültigen IBAN den String `IBAN is valid` aus, ansonsten `IBAN is invalid` und beenden Sie das Programm mit dem Rückgabewert 0.

Eine IBAN wird folgendermaßen berechnet:

Zunächst wird das Länderkennzeichen "DE" als Zahlenwert (D=13, E=14) an die Ziffern der Kontoidentifikation angehängt. Dadurch entsteht eine 22 Ziffern lange Zahl. An diese Zahl werden dann die Prüfziffern angehängt, wodurch eine insgesamt 24 Ziffern lange Zahl entsteht. Anschließend wird diese Zahl dann mit Rest durch 97 geteilt. Wenn das Ergebnis 1 ist, ist die IBAN gültig, andernfalls nicht.

Hinweise: Sie benötigen zum Teilen mit Rest den Modulo-Operator (`%` auf der Tastatur). Die gesamte Zahl ist zu groß für einen Integer in C. Gehen Sie daher folgendermaßen vor: Starten Sie mit rest = 0. Gehen Sie dann Ziffer für Ziffer vor, indem sie den Rest mit 10 multiplizieren, die nächste Ziffer addieren und anschließend Modulo 97 rechnen, um den Rest neu zu berechnen. Oder um es noch kompakter darzustellen: `neuer_rest = (alter_rest * 10 + ziffer) % 97`

Kopieren Sie den gesamten Quellcode von **iban.c** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

## A4: ID3 Tags (13 Punkte)

Das Audioformat MP3 verfügte anfangs über keine Möglichkeit, Informationen über den Inhalt einer Datei zu speichern. Im Jahr 1997 erfand Eric Kemp mit ID3 ein einfaches Metadatenformat, das aus einem 128 Byte langen *Tag* (also einer Datenstruktur mit einem definiertem Format) besteht, der am Ende einer Audiodatei angehängt wird. In diesem Tag werden Informationen wie Interpret, Album und Songname gespeichert.

Die Dateien für diese Aufgabe finden Sie im Verzeichnis **exam-files/id3** in Ihrem Home-Verzeichnis.

### A4a (7 Punkte)

Vervollständigen Sie das Programm **id3v1.c**, um den ID3-Tag aus dem Song *file.mp3* zu extrahieren. Berücksichtigen Sie dazu die folgenden Informationen.

In der ursprünglichen ID3-Version 1.0 war ein ID3-Tag wie folgt spezifiziert:

```text
MP3 ID3 Tag 1.0 -- 128 bytes
(c) 1996, 1997 NamkraD (Eric Kemp)
Seek end of file, then -128, if first 3 bytes='TAG' then it has info layed
out as follows...

0..2 == 'TAG' (3 Bytes)
3..32 == Song Name (30 bytes)
33..62 == Artist (30 Bytes)
63..92 == Album Name (30 Bytes)
93..96 == Year (4 Bytes)
97..126 == Comment (30 Bytes)
127 == 1 Byte Song Genre Identifier
(get the listing of song Genre Types to know what byte = what)
```

Definieren Sie in *id3v1.c* eine **struct**, die dem obigen Schema entspricht, lesen Sie die Informationen aus der Datei *file.mp3* in die *struct* ein und geben Sie die struct-Attribute mit den bereits vorhandenen printf-Anweisungen aus.

Hinweis: Für das Genre-Byte können Sie den Datentyp *uint8\_t* verwenden. Für die restlichen Felder eignet sich *char*.

Kopieren Sie den gesamten Inhalt von **id3v1.c** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

Ein Freund hat die Aufgabe mit den vorgegebenen printf-Anweisungen gelöst, beobachtet jedoch folgende Ausgabe:

```text
...
Year: 1987:)
Comment: :)
...
```

Wie zu sehen ist, wird der Inhalt des Kommentar-Felds auch beim Jahr ausgegeben. Warum?

*[Antwortfeld, mehrzeilig]*

### A4b (6 Punkte)

ID3 wurde von M. Nilsson zu ID3v2 weiterentwickelt. In ID3v2 können komplexere Daten abgelegt werden, unter anderem auch ein Album-Cover-Bild. Ihre Aufgabe ist es im Folgenden, das Album-Cover-Bild des Songs zu extrahieren.

Ein ID3v2-Tag befindet sich am Anfang einer Datei und besteht aus einem ID3v2-Header-Tag, in dem einer oder mehrere ID3v2-Frames eingebettet sind. Diese ID3v2-Frames enthalten jeweils einen Header, der die Art der Metadaten beschreibt, sowie die Metadaten selbst.

Im vorliegenden Fall (Datei: **id3v2.c**) ist bereits Code vorhanden, der den ID3v2-Header und den Attached-Picture (APIC) Frame einliest. Der APIC-Frame wird hierbei in den Heap eingelesen und über den Pointer *frame\_data* referenziert. Ihre Aufgabe ist es nun, die Metadaten des APIC-Headers zu überspringen und anschließend das Album-Cover-Bild in der Datei **cover.png** abzuspeichern.

Ein APIC-Frame sieht folgendermaßen aus:

```text
Text encoding   $xx (1 Byte)
MIME type       <text string>'\0' (String, terminiert durch Nullbyte)
Picture type    $xx (1 Byte)
Description     <text string>'\0' (String, terminiert durch Nullbyte)
Picture data    <binary data>
```

Hinweis: Sollte es Ihnen nicht gelingen, den korrekten Offset zu ermitteln, können Sie stattdessen den Wert 42 verwenden.

Kopieren Sie den gesamten Inhalt von **id3v2.c** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

## A5: Datenstrukturen und Algorithmen (14 Punkte)

### A5a (3 Punkte)

Ein unsortiertes Array enthält folgende Elemente **\[5, 4, 2, 1, 3\]**. Sie beobachten die ersten zwei Durchläufe der äußeren Schleife eines Sortieralgorithmus, der auf dieses Array angewendet wird.

```text
Nach Durchlauf 1: [4, 2, 1, 3, 5]

Nach Durchlauf 2: [2, 1, 3, 4, 5]
```

Welcher der beiden Algorithmen – Bubble Sort oder Selection Sort – hat diese Zustände produziert? Auf welche Beobachtung stützen Sie Ihre Entscheidung?

Geben Sie in der ersten Zeile den Algorithmus an, ab der zweiten Zeile Ihre Begründung.

*[Antwortfeld, mehrzeilig]*

### A5b (6 Punkte)

Zur Erinnerung: Ein **Trie** kann platzsparend mit folgender Datenstruktur implementiert werden:

```c
struct trie_node {
  char buchstabe;
  char* wort; // Zeiger auf das gespeicherte Wort (falls vorhanden; sonst NULL)
  struct trie_node* nachbar;
  struct trie_node* kind;
};
```

Mit dieser Datenstruktur wurde ein Trie für Abkürzungen aufgebaut, der bereits folgende Einträge enthält:

- HTML: Hypertext Markup Language
- HTTP: Hypertext Transfer Protocol

Die Speicherrepräsentation sieht wie folgt aus (Adressen sind vierstellig):

```c
// Root-Knoten (zeigt auf den ersten Buchstaben)
struct trie_node* root = 0x1000;

0x1000: ['H', NULL, NULL, 0x2000]
0x2000: ['T', NULL, NULL, 0x3000]
0x3000: ['T', NULL, 0x7000, 0x5000]
0x5000: ['P', Zeiger auf "Hypertext Transfer Protocol", NULL, NULL]
0x7000: ['M', NULL, NULL, 0x8000]
0x8000: ['L', Zeiger auf "Hypertext Markup Language", NULL, NULL]
```

Als nächstes soll der Eintrag **HTTPS: Hypertext Transfer Protocol Secure** in den Trie eingefügt werden. Eine Kollegin sagt: *Moment mal, der aktuelle Trie enthält nur 4-Buchstaben-Abkürzungen, aber jetzt fügen wir eine 5-Buchstaben-Abkürzung ein. Das bedeutet, wir müssen die vorherigen Inhalte aktualisieren und ein spezielles End-of-Entry-Zeichen anhängen, sonst wird der Trie den String „Hypertext Transfer Protocol“ zurückgeben, wenn wir später die neue Abkürzung „HTTPS“ nachschlagen.*

Ist die Aussage Ihrer Kollegin korrekt? Antworten Sie in der ersten Zeile mit Ja oder Nein. Begründen Sie ab der zweiten Zeile.

*[Antwortfeld, mehrzeilig]*

Erweitern Sie nun die Trie-Struktur um den neuen Eintrag **HTTPS: Hypertext Transfer Protocol Secure**. Verwenden Sie die Adresse **0x9000** für den neuen Knoten. Vervollständigen Sie die Felder der betroffenen Knoten in der Notation `[buchstabe, wort, nachbar, kind]`:

```text
Geänderter Knoten:
0x5000: [___, ___, ___, ___]

Neuer Knoten:
0x9000: [___, ___, ___, ___]
```

Geben Sie die vollständigen Inhalte der beiden Knoten an.

*[Antwortfeld, mehrzeilig]*

Erweitern Sie die Trie-Struktur zusätzlich um den Eintrag **JSON: JavaScript Object Notation**. Verwenden Sie die Adressen **0xA000** bis **0xD000** für die neuen Knoten. Orientieren Sie sich dabei am vorgegebenen Antwortschema und ändern Sie nur die dort angegebenen Knoten.

```text
Geänderter Knoten:
0x1000: [___, ___, ___, ___]

Neue Knoten:
0xA000: [___, ___, ___, ___]
0xB000: [___, ___, ___, ___]
0xC000: [___, ___, ___, ___]
0xD000: [___, ___, ___, ___]
```

Geben Sie die vollständigen Inhalte aller Knoten an.

*[Antwortfeld, mehrzeilig]*

### A5c (5 Punkte)

Unten stehen 11 nummerierte Codezeilen für ein C-Programm, das mit *malloc* Speicher für vier Ganzzahlen allokiert, die Werte 40, 30, 20, 10 zuweist und sie in umgekehrter Reihenfolge ausgibt. Acht Zeilen ergeben das korrekte Programm – die restlichen drei gehören nicht dazu. Zwei Zeilen enthalten Lücken (LUECKE\_1 und LUECKE\_2), die jeweils das Argument für *malloc* darstellen. Ziffer 8 fügt die *zwei* angegebenen Zeilen hintereinander ein.

Beide Lücken enthalten denselben Ausdruck; relevant ist nur die korrekte *malloc*-Zeile.

**Erwartete Ausgabe:**

```text
Wert 0: 10
Wert 1: 20
Wert 2: 30
Wert 3: 40
```

```c
 1: return 0; }
 2: printf("Wert %d: %d\n", i, arr[i]); }
 3: free(arr);
 4: int *arr = malloc(LUECKE_1);
 5: for (int i = 0; i < 4; i++) {
 6: arr[0] = 40; arr[1] = 30; arr[2] = 20; arr[3] = 10;
 7: int main(void) {
 8: #include <stdio.h>
    #include <stdlib.h>
 9: printf("Wert %d: %d\n", 3 - i, arr[i]); }
10: for (int i = 3; i >= 0; i--) {
11: int arr[4] = malloc(LUECKE_2);
```

Reihenfolge der korrekten Zeilen (kommasepariert, z.B. 8, 7, 4, ...):

*[Antwortfeld]*

Was muss in die Lücke eingesetzt werden, damit die korrekte malloc-Zeile funktioniert?

*[Antwortfeld, mehrzeilig]*

## A6: Zeiger und Datenstrukturen (7 Punkte)

### A6a (4 Punkte)

Ein C-Programm soll die Werte zweier Variablen mithilfe einer Funktion vertauschen. Unten stehen 16 nummerierte Codezeilen. Zehn davon ergeben das korrekte Programm – die restlichen sechs gehören nicht dazu. Zwei Zeilen enthalten Lücken: LUECKE\_1 hat die Struktur `??? = ???` und LUECKE\_2 besteht aus den Argumenten des Funktionsaufrufs.

```c
 1: int temp = a;
 2: #include <stdio.h>
 3: void vertausche(int *a, int *b) {
 4: return a; }
 5: int x = 15; int y = 25;
 6: LUECKE_1;
 7: vertausche(x, y);
 8: int main(void) {
 9: *b = temp; }
10: int temp = *a;
11: a = b;
12: printf("Nach dem Tausch: x = %d, y = %d\n", x, y);
13: b = temp;
14: int vertausche(int a, int b) {
15: vertausche(LUECKE_2);
16: return 0; }
```

Reihenfolge der korrekten Zeilen (kommasepariert, z.B. 2, 3, 10, ...):

*[Antwortfeld]*

Was muss in LUECKE\_1 bzw. LUECKE\_2 stehen, damit die jeweilige Zeile korrekt ist?

*[Antwortfeld, mehrzeilig]*

### A6b (3 Punkte)

Eine Programmiererin oder ein Programmierer hat versucht, eine Funktion zum Löschen eines Elements aus einer einfach verketteten Liste zu implementieren. Die Implementierung enthält jedoch einen Fehler, der dazu führt, dass das Löschen des ersten Elements nicht korrekt funktioniert.

Hier ist die fehlerhafte Implementierung:

```c
#include <stdbool.h>
#include <stdlib.h>

typedef struct sllnode {
    int value;
    struct sllnode *next;
} sllnode;

// TODO: Fehler beim Löschen des ersten Elements beheben
bool delete_element(sllnode* head, int value) {
    if (head == NULL) {
        return false; // Liste ist leer
    }

    // Erstes Element löschen
    if (head->value == value) {
        sllnode* temp = head;
        head = head->next;
        free(temp);
        return true;
    }

    // Anderes Element löschen
    sllnode* current = head;
    while (current->next != NULL) {
        if (current->next->value == value) {
            sllnode* temp = current->next;
            current->next = temp->next;
            free(temp);
            return true;
        }
        current = current->next;
    }
    return false; // Element nicht gefunden
}
```

Beheben Sie den Fehler, indem Sie einen Doppelpointer (`sllnode**`) verwenden. Sie müssen dazu auch die Signatur der Funktion *delete\_element* verändern. Fügen Sie den Code der korrigierten Funktion *delete\_element* hier ein:

*[Antwortfeld, mehrzeilig]*

## B1: Python und OOP (20 Punkte)

### B1a (4 Punkte)

Für ein Projekt haben Sie einen Mikrocontroller mit verschiedenen Sensoren ausgestattet, um deren Werte zu erfassen. Die gesammelten Messdaten sollen in Python ausgewertet werden.

Die Klasse *Measurement* (Datei **oop.py** im Verzeichnis **exam-files/python\_oop**) dient als allgemeine Vorlage für Sensormessungen. Der Code unten zeigt die Klasse mit zwei Lücken. Füllen Sie diese aus.

```python
class Measurement:
    def __init__(self, sensor_id, value, time):
        self.sensor_id = sensor_id
        self.value = value
        self.time = time

    def __str__(self):
        ___Lücke_1___

___Lücke_2___
```

Lücke 1 ist die Implementierung der `__str__`-Methode (eine Zeile genügt). In Lücke 2 erstellen Sie ein Objekt mit den folgenden Attributwerten und geben es mit `print()` aus:

```text
value = 22.3
time = "2025-07-17T08:15:00"
sensor_id = "T1"
```

Die Ausgabe von `print(obj)` soll so aussehen:

```text
2025-07-17 08:15:00 Sensor T1: 22.3
```

Lücke 1: Implementierung von `__str__` (eine Zeile):

*[Antwortfeld]*

Lücke 2: Objekt erstellen und mit `print()` ausgeben:

*[Antwortfeld, mehrzeilig]*

### B1b (6 Punkte)

Nutzen Sie die erweiterte Klasse *Measurement* aus der Datei **subclasses.py** (Verzeichnis **exam-files/python\_oop**) als Basisklasse. Der Code unten zeigt eine Subklasse mit drei Lücken.

- **Lücke 1** enthält die Klassendefinitionszeile mit Vererbung für die Klasse `TemperatureMeasurement`.
- **Lücke 2** enthält eine **Methode *is\_valid()***. Diese gibt *True* zurück, wenn der Messwert im gültigen Bereich liegt (Temperatur: –20 bis +60), sonst *False*.
- **Lücke 3** ist eine **Property *unit***. Diese gibt die passende Einheit zurück (bei Temperatur: “°C”).

```python
___Lücke_1___
    measurement_type = "temperature"

    ___Lücke_2___

    ___Lücke_3___
```

Tragen Sie hier den Inhalte von Lücke 1 ein:

*[Antwortfeld]*

Tragen Sie hier den Inhalte von Lücke 2 ein:

*[Antwortfeld, mehrzeilig]*

Tragen Sie hier den Inhalte von Lücke 3 ein:

*[Antwortfeld, mehrzeilig]*

### B1c (6 Punkte)

Für diese Aufgabe benötigen Sie die Dateien **read\_measurements.py** und **measurements.json**. Beide Dateien befinden sich ebenfalls im Verzeichnis **exam-files/python\_oop**.

Implementieren Sie die Funktion **load\_measurements\_from\_json**, die die JSON-Datei einliest und für jeden Eintrag in der JSON-Datei das passende Messobjekt erstellt. Das heißt, es soll je nach Typ (“temperature” oder “co2”) eine Instanz der Klasse *TemperatureMeasurement* oder *CarbonDioxideMeasurement* erzeugt werden. Sie können davon ausgehen, dass die Werte in der JSON-Datei in °C (Grad Celsius) bzw. ppm (parts per million) vorliegen.

Kopieren Sie den gesamten Inhalt von **read\_measurements.py** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

In **main()** geben Sie die erzeugten Objekte anschließend **sortiert nach ihrem Zeitstempel** aus. Zur Erinnerung: `sorted()` akzeptiert einen `key`-Parameter, der bestimmt, wonach sortiert wird:

```python
sorted(["Hund", "Katze", "Maus"], key=lambda x: len(x))
```

Die Zeitstempel in der JSON-Datei sind bereits so formatiert, dass eine Sortierung als String ausreicht.

Dabei sollen die Attributwerte in folgender Reihenfolge ausgeben werden: time, sensor\_id, value, unit.

Rufen Sie zudem – abhängig vom Typ des Messobjekts – die passende Methode auf: bei Temperaturmessungen `convert_celsius_to_fahrenheit()` und bei CO₂-Messungen `get_air_status()`. Hängen Sie den Rückgabewert dieser Methoden jeweils ans Ende der Zeile an.

Kopieren Sie den gesamten Inhalt von **read\_measurements.py** in dieses Feld.

*[Antwortfeld, mehrzeilig]*

### B1d (4 Punkte)

Betrachten Sie die folgende Python-Interpreter-Sitzung:

```python
>>> import random
>>> import string
>>> string.ascii_letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> random.choice(string.ascii_letters)
'k'
>>> random.choice(string.ascii_letters)
'T'
>>> "".join([random.choice(string.ascii_letters) for _ in range(6)])
'SRTGlv'
```

Wie nennt man das Python-Konstrukt `[... for ... in ...]`, das hier als Argument von `join()` verwendet wird?

*[Antwortfeld]*

Schreiben Sie Python-Code, der **ohne dieses Konzept** einen zufälligen Code aus 6 Buchstaben erzeugt und in der Variable `code` speichert. Verwenden Sie stattdessen eine `for`-Schleife, um nach und nach zufällige Zeichen an `code` anzuhängen.

Geben Sie den Code mit `for`-Schleife hier ein:

*[Antwortfeld, mehrzeilig]*

## B2: Web-Entwicklung: Link-Shortener (20 Punkte)

In den folgenden Teilaufgaben entwickeln Sie einen **Link Shortener** mit Flask. Sie können dort eine URL eingeben und erhalten einen kurzen Code, über den die URL später wieder aufgerufen werden kann. Die Zuordnungen von URLs und Codes werden vom Server in einer JSON-Datei gespeichert.

Das Skelett der Anwendung finden Sie im Verzeichnis **exam-files/shortener**. Öffnen Sie die Datei `app.py` und die Templates im Ordner `templates/` – dort finden Sie vorbereitete Funktionen und Routen mit `TODO`-Kommentaren, die Sie Schritt für Schritt vervollständigen.

Damit Code-Änderungen vom Flask-Server automatisch berücksichtigt werden, müssen Sie den Server wie folgt starten:

**`flask --debug run`**

Um die Anwendung im Browser zu öffnen, klicken Sie bei gedrückter <kbd>Strg</kbd>-Taste auf die URL (*Running on ...*), die Flask nach dem Start anzeigt. Um Flask zu beenden, verwenden Sie die Tastenkombination <kbd>Strg-C</kbd>.

### B2a (4 Punkte)

Vervollständigen Sie das Template `index.html`. Erstellen Sie ein HTML-Formular mit:

- Methode POST, Ziel beim Submit `/shorten`
- Eingabefeld für die URL (Name: `url`)
- Eingabefeld für den optionalen Kurzcode (Name: `code`)
- Absende-Button mit Text **Shorten!**

Geben Sie den HTML-Code für das Formular hier ein (nur das Formular; nicht die ganze Datei):

*[Antwortfeld, mehrzeilig]*

### B2b (6 Punkte)

Vervollständigen Sie in `app.py` die Funktion `write_links(links)` sowie die Route `/shorten`.

**`write_links`** ist das Gegenstück zu `read_links()` (die bereits fertig in `app.py` steht): Die Funktion soll das übergebene Dictionary `links` als JSON in die Datei schreiben.

Tragen Sie hier den Code der Funktion `write_links(links)` ein (nicht die ganze Datei):

*[Antwortfeld, mehrzeilig]*

**Route `/shorten`:** Die Route empfängt einen POST-Request. Implementieren Sie die folgenden Schritte:

1. Die Formularfelder `url` und `code` aus dem Request auslesen (mit `request.form[...]`)
2. Falls `code` leer ist: einen zufälligen 6-Buchstaben-Code erzeugen (siehe Hilfskommentar am Ende von `app.py`)
3. Die bestehenden Links mit `read_links()` laden, den neuen Eintrag `code → url` hinzufügen und mit `write_links()` speichern
4. Das Template `result.html` rendern und dabei die Variablen `code` und `url` übergeben

Tragen Sie hier den Code der Route `/shorten` ein (nicht die ganze Datei):

*[Antwortfeld, mehrzeilig]*

**Tipp zum Testen:** Sie können die Route auch ohne funktionierendes Formular testen. Der Befehl

`curl -X POST -d "url=https://example.com&code=test1" http://...`

sendet eine HTTP-POST-Anfrage mit Formulardaten an die URL am Ende des curl-Aufrufs. An welche Ziel-IP-Adresse und welchen Ziel-Port müssen Sie diese Anfrage senden, wenn Sie Flask mit `flask --debug run` (ohne zusätzliche Parameter) starten? Geben Sie die vollständige URL inklusive Pfad an.

Vollständige URL für den curl-Befehl:

*[Antwortfeld]*

### B2c (5 Punkte)

**Redirect-Route:** Schreiben Sie eine neue Route in `app.py`, die unter `/CODE` erreichbar ist, wobei `CODE` ein variabler Kurzcode ist (z.B. `/abc123` oder `/test1`). Die Route soll:

1. Die gespeicherten Links mit `read_links()` laden
2. Falls der Kurzcode im Dictionary existiert: mit `redirect()` zur gespeicherten URL weiterleiten
3. Falls nicht: `"Link nicht gefunden"` mit Statuscode 404 zurückgeben

Tragen Sie hier Ihren Code inklusive Decorator ein (nicht das ganze Python-Programm):

*[Antwortfeld, mehrzeilig]*

**Tipp zum Testen:** Legen Sie über das Formular (oder per curl) einen Link mit dem Code `infdoc` an, der auf `http://127.0.0.1:8080/infeinf-docs.html` verweist. Wenn Ihre Redirect-Route funktioniert, sollte der Aufruf von `/infdoc` im Browser zur Dokumentationsseite weiterleiten.

### B2d (5 Punkte)

**Jinja-Template:** Schreiben Sie das vollständige Template `links.html`, sodass alle gespeicherten Links als Tabellenzeilen angezeigt werden. Die Variable `links` wird als Dictionary übergeben (Kurzcode → URL). Jede Zeile soll den Code und die URL (als klickbaren Link) enthalten.

Tragen Sie hier den gesamten Inhalt der Datei `links.html` ein:

*[Antwortfeld, mehrzeilig]*
