Files
oscars-dojo/data/programming_exercises.ts
2025-07-05 00:07:04 +02:00

286 lines
46 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Exercise } from '../types';
export const programmingExercises: Exercise[] = [
{
id: 'prog-ex1',
title: 'Programmieren: Zahlenraten',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `GenZufall(void)` mit Rückgabetyp `int`, welche mithilfe der Funktion `rand()` aus `<stdlib.h>` eine zufällige Zahl zwischen 1 und 128 generiert und zurückgibt.',
solution: 'int GenZufall(void) {\n return (rand() % 128) + 1;\n}',
points: 5,
type: 'function-definition',
},
{
id: 'b',
prompt: 'Erstellen Sie eine C-Funktion `PruefeZahl` mit Rückgabetyp `int` und zwei Parametern `int ziel`, `int rat`. Die Funktion soll die beiden Zahlen vergleichen:\n- wenn `rat > ziel`: Ausgabe "Zahl zu gross!" und Rückgabewert 1\n- wenn `rat < ziel`: Ausgabe "Zahl zu klein!" und Rückgabewert -1\n- wenn `rat = ziel`: keine Ausgabe und Rückgabewert 0',
solution: 'int PruefeZahl(int ziel, int rat) {\n if (rat > ziel) {\n printf("Zahl zu gross!\\n");\n return 1;\n } else if (rat < ziel) {\n printf("Zahl zu klein!\\n");\n return -1;\n } else {\n return 0;\n }\n}',
points: 7,
type: 'function-definition',
},
{
id: 'c',
prompt: 'Erstellen Sie nun ein C-Programm inkl. `main`. Generieren Sie eine Zufallszahl `ziel`. In einer Schleife wird vom Benutzer die Zahl `rat` eingelesen und mit `PruefeZahl()` verglichen. Die Eingabe wird wiederholt, bis die richtige Zahl erraten wurde oder 7 Versuche erreicht sind. Geben Sie am Ende das Ergebnis aus: "Erraten in %i Versuchen!" oder "Zu viele Versuche. Die gesuchte Zahl war %i."',
solution: '#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\nint GenZufall(void);\nint PruefeZahl(int ziel, int rat);\n\nint main() {\n int ziel, rat, erg, vers = 0;\n srand(time(NULL));\n ziel = GenZufall();\n\n printf("Ich habe eine Zahl zwischen 1 und 128. Du hast 7 Versuche.\\n");\n\n for (vers = 1; vers <= 7; vers++) {\n printf("Versuch %d: Gib deine Zahl ein: ", vers);\n scanf("%d", &rat);\n erg = PruefeZahl(ziel, rat);\n if (erg == 0) {\n break;\n }\n }\n\n if (erg == 0) {\n printf("Erraten in %d Versuchen!\\n", vers);\n } else {\n printf("Zu viele Versuche. Die gesuchte Zahl war %d.\\n", ziel);\n }\n\n return 0;\n}',
points: 12,
type: 'main-function',
},
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert ein einfaches Zahlenratespiel.\n\n**Teil a)**\n```c\nint GenZufall(void) {\n // rand() % 128 erzeugt eine Zahl von 0-127.\n // +1 verschiebt den Bereich auf 1-128.\n return (rand() % 128) + 1;\n}\n```\nDie Funktion `GenZufall` nutzt den Modulo-Operator, um den riesigen Zahlenbereich von `rand()` auf den gewünschten Bereich zu begrenzen.\n\n**Teil b)**\n```c\nint PruefeZahl(int ziel, int rat) {\n if (rat > ziel) {\n printf("Zahl zu gross!\\n");\n return 1;\n } else if (rat < ziel) {\n printf("Zahl zu klein!\\n");\n return -1;\n } else {\n return 0; // Zahlen sind gleich\n }\n}\n```\n`PruefeZahl` kapselt die Vergleichslogik und gibt je nach Ergebnis einen unterschiedlichen Wert zurück. Dies macht den Code in `main` sauberer und besser lesbar.\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n// Prototypen\nint GenZufall(void);\nint PruefeZahl(int ziel, int rat);\n\nint main() {\n int ziel, rat, erg, vers = 0;\n // Zufallsgenerator initialisieren\n srand(time(NULL));\n ziel = GenZufall();\n\n for (vers = 1; vers <= 7; vers++) {\n printf("Versuch %d: Gib deine Zahl ein: ", vers);\n scanf("%d", &rat);\n erg = PruefeZahl(ziel, rat);\n if (erg == 0) {\n break; // Schleife verlassen bei Erfolg\n }\n }\n\n if (erg == 0) {\n printf("Erraten in %d Versuchen!\\n", vers);\n } else {\n printf("Zu viele Versuche. Die gesuchte Zahl war %d.\\n", ziel);\n }\n return 0;\n}\n```\nDie `main`-Funktion steuert den Spielfluss. `srand(time(NULL))` ist wichtig, damit bei jedem Programmlauf neue Zufallszahlen erzeugt werden. Die `for`-Schleife begrenzt die Anzahl der Versuche. Ein `break` beendet die Schleife vorzeitig, wenn die richtige Zahl geraten wurde.'
},
{
id: 'prog-ex2',
title: 'Programmieren: Vokaleraten (Glücksrad)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `ErsetzeVokal(char ch, char newch)` mit Rückgabetyp `char`. Sie soll prüfen, ob `ch` ein Vokal ist (Groß- & Kleinschreibung). Wenn ja, wird `newch` zurückgegeben, sonst `ch`.',
solution: 'char ErsetzeVokal(char ch, char newch) {\n switch(ch) {\n case \'a\':\n case \'e\':\n case \'i\':\n case \'o\':\n case \'u\':\n case \'A\':\n case \'E\':\n case \'I\':\n case \'O\':\n case \'U\':\n return newch;\n default:\n return ch;\n }\n}',
points: 6,
type: 'function-definition',
},
{
id: 'b',
prompt: 'Erstellen Sie eine C-Funktion `ErzeugeLuecken(char text[])` ohne Rückgabewert. Sie soll den String `text` durchlaufen und jedes Zeichen mit `ErsetzeVokal()` prüfen, um Vokale durch einen Unterstrich `_` zu ersetzen.',
solution: 'void ErzeugeLuecken(char text[]) {\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = ErsetzeVokal(text[i], \'_\');\n }\n}',
points: 6,
type: 'function-definition',
},
{
id: 'c',
prompt: 'Erstellen Sie ein `main`-Programm, das einen Text (max. 30 Zeichen) einliest. Rufen Sie `ErzeugeLuecken()` auf, um die Vokale zu ersetzen. Geben Sie zum Schluss den modifizierten Text aus.\n\n**Hinweis:** Nutzen Sie `fgets(buffer, size, stdin)` für eine sichere Eingabe. Denken Sie daran, dass `fgets` auch das Zeilenumbruch-Zeichen (`\\n`) mit einliest, das Sie eventuell entfernen müssen.',
solution: '#include <stdio.h>\n#include <string.h>\n\nchar ErsetzeVokal(char ch, char newch);\nvoid ErzeugeLuecken(char text[]);\n\nint main() {\n char text[32]; // 30 chars + newline + null\n printf("Bitte Text eingeben (max. 30 char): ");\n fgets(text, sizeof(text), stdin);\n\n // Remove newline character if present\n text[strcspn(text, "\\n")] = 0;\n\n ErzeugeLuecken(text);\n printf("Text ohne Vokale: %s\\n", text);\n\n return 0;\n}',
points: 12,
type: 'main-function',
},
],
explanation: '### Lösungserklärung\n\nDieses Programm wandelt einen Text so um, wie man es vom "Glücksrad" kennt.\n\n**Teil a)**\n```c\nchar ErsetzeVokal(char ch, char newch) {\n // Die switch-Anweisung prüft den Buchstaben.\n // Bei Vokalen wird newch zurückgegeben.\n switch(ch) {\n case \'a\':\n case \'e\':\n case \'i\':\n case \'o\':\n case \'u\':\n case \'A\':\n case \'E\':\n case \'I\':\n case \'O\':\n case \'U\':\n return newch;\n // default wird ausgeführt, wenn keiner der cases zutrifft.\n default:\n return ch;\n }\n}\n```\n`ErsetzeVokal` nutzt ein `switch`-Statement mit "fall-through", um effizient zu prüfen, ob ein Zeichen ein Vokal ist. Dies ist übersichtlicher als eine lange `if-else-if`-Kette.\n\n**Teil b)**\n```c\nvoid ErzeugeLuecken(char text[]) {\n // Schleife läuft, bis das String-Ende-Zeichen \'\\0\' erreicht ist.\n for (int i = 0; text[i] != \'\\0\'; i++) {\n // Das Zeichen im Text wird direkt überschrieben.\n text[i] = ErsetzeVokal(text[i], \'_\');\n }\n}\n```\n`ErzeugeLuecken` iteriert mit einer `for`-Schleife durch den String und wendet die Logik aus Teil a) auf jedes Zeichen an. Da Strings in C (char-Arrays) direkt modifiziert werden können, ist kein Rückgabewert nötig (call by reference).\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <string.h>\n\n// Prototypen\nchar ErsetzeVokal(char ch, char newch);\nvoid ErzeugeLuecken(char text[]);\n\nint main() {\n // Puffer für 30 Zeichen + Newline + Nullterminator\n char text[32]; \n printf("Bitte Text eingeben (max. 30 char): ");\n fgets(text, sizeof(text), stdin);\n\n // Entfernt das Newline-Zeichen, das fgets mit einliest.\n text[strcspn(text, "\\n")] = 0;\n\n ErzeugeLuecken(text);\n printf("Text ohne Vokale: %s\\n", text);\n\n return 0;\n}\n```\nDie `main`-Funktion liest einen String sicher mit `fgets` ein, um Buffer Overflows zu vermeiden. `strcspn` ist eine nützliche Funktion aus `string.h`, um das oft störende Newline-Zeichen am Ende des `fgets`-Inputs zu finden und zu entfernen.'
},
{
id: 'prog-ex3',
title: 'Programmieren: Hangman',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `CheckWon(int remain, int lives)`, die prüft, ob gewonnen oder verloren wurde. Wenn `remain == 0` und `lives > 0`, gib "Gewonnen" aus, ansonsten "Leider verloren".',
solution: 'void CheckWon(int remain, int lives) {\n if (remain == 0 && lives > 0) {\n printf("Gewonnen\\n");\n } else {\n printf("Leider verloren\\n");\n }\n}',
points: 4,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Erstellen Sie eine Funktion `TestChar(char word[], char quest[], int len, char c, int *remain)` mit `int`-Rückgabe. Sie durchläuft `word`. Wenn `word[i] == c` und `quest[i] == \'-\'`, wird `quest[i]` auf `c` gesetzt, `*remain` dekrementiert und `found` auf 1 gesetzt. Geben Sie am Ende `found` zurück.',
solution: 'int TestChar(char word[], char quest[], int len, char c, int *remain) {\n int found = 0;\n for (int i = 0; i < len; i++) {\n if (word[i] == c && quest[i] == \'-\') {\n quest[i] = c;\n (*remain)--;\n found = 1;\n }\n }\n return found;\n}',
points: 7,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie die `main`-Funktion für Hangman. Wort einlesen (max 10 Zeichen), Länge `len` ermitteln. Ratewort `quest` mit Bindestrichen füllen. In einer Schleife (solange `lives > 0` und `remain > 0`): Leben ausgeben, Buchstabe `c` einlesen, `TestChar()` aufrufen. Wenn `TestChar` 0 zurückgibt, `lives` dekrementieren. `quest` ausgeben. Am Ende `CheckWon()` aufrufen.\n\n**Hinweise:** Nutzen Sie `strlen()` aus `<string.h>` für die Länge. Beim Einlesen eines einzelnen Zeichens mit `scanf()` verwenden Sie am besten `scanf(" %c", &c)` (mit Leerzeichen davor), um Zeilenumbrüche aus der vorherigen Eingabe zu ignorieren.',
solution: '#include <stdio.h>\n#include <string.h>\n\nvoid CheckWon(int remain, int lives);\nint TestChar(char word[], char quest[], int len, char c, int *remain);\n\nint main(void) {\n char word[11];\n char quest[11];\n int len = 0;\n char c = \'\\0\';\n int remain = 0;\n int lives = 5;\n\n printf("Geben Sie das zu suchende Wort ein (max. 10 Buchstaben): ");\n scanf("%10s", word);\n len = strlen(word);\n remain = len;\n\n for (int i = 0; i < len; i++) {\n quest[i] = \'-\';\n }\n quest[len] = \'\\0\';\n\n printf("Das Wort hat %d Buchstaben: %s\\n", len, quest);\n\n while (lives > 0 && remain > 0) {\n printf("Leben: %d. Buchstabe eingeben: ", lives);\n scanf(" %c", &c); // Leerzeichen vor %c um Whitespace zu konsumieren\n\n if (TestChar(word, quest, len, c, &remain) == 0) {\n lives--;\n }\n printf("%s\\n", quest);\n }\n\n CheckWon(remain, lives);\n\n return 0;\n}',
points: 13,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm ist eine einfache Implementierung des Spiels "Hangman".\n\n**Teil a)**\n```c\nvoid CheckWon(int remain, int lives) {\n if (remain == 0 && lives > 0) {\n printf("Gewonnen\\n");\n } else {\n printf("Leider verloren\\n");\n }\n}\n```\n`CheckWon` ist eine simple Funktion, die nur das Spielende basierend auf den finalen Werten für `remain` (übrige Buchstaben) und `lives` (übrige Leben) auswertet.\n\n**Teil b)**\n```c\nint TestChar(char word[], char quest[], int len, char c, int *remain) {\n int found = 0;\n for (int i = 0; i < len; i++) {\n // Prüfe auf Übereinstimmung UND ob der Buchstabe noch nicht gefunden wurde.\n if (word[i] == c && quest[i] == \'-\') {\n quest[i] = c;\n (*remain)--; // Dereferenzierung des Zeigers, um den Wert in main zu ändern.\n found = 1;\n }\n }\n return found;\n}\n```\n`TestChar` ist die Kernlogik des Spiels. Sie prüft nicht nur, ob ein Buchstabe im Wort vorkommt, sondern auch, ob er nicht bereits aufgedeckt wurde (`quest[i] == \'-\'`). Die Verwendung eines Zeigers auf `remain` (`int *remain`) erlaubt es der Funktion, eine Variable in `main` direkt zu modifizieren (`call by reference`).\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <string.h>\n\n// Prototypen\nvoid CheckWon(int remain, int lives);\nint TestChar(char word[], char quest[], int len, char c, int *remain);\n\nint main(void) {\n char word[11], quest[11];\n int len = 0, remain = 0, lives = 5;\n char c = \'\\0\';\n\n scanf("%10s", word);\n len = strlen(word);\n remain = len;\n\n for (int i = 0; i < len; i++) quest[i] = \'-\';\n quest[len] = \'\\0\';\n\n while (lives > 0 && remain > 0) {\n printf("%s\\nLeben: %d. Buchstabe: ", quest, lives);\n scanf(" %c", &c); // Leerzeichen vor %c konsumiert Whitespace (z.B. Enter)\n\n if (TestChar(word, quest, len, c, &remain) == 0) {\n lives--;\n }\n }\n\n CheckWon(remain, lives);\n return 0;\n}\n```\nDie `main`-Funktion initialisiert das Spiel, indem sie das Ratewort `quest` mit Bindestrichen füllt. Die `while`-Schleife läuft, solange das Spiel aktiv ist. Am Ende wird `CheckWon` aufgerufen, um das Ergebnis anzuzeigen. Das Leerzeichen in `scanf(" %c", &c)` ist ein wichtiger Trick, um übrig gebliebene Newline-Zeichen aus vorherigen Eingaben zu ignorieren.'
},
{
id: 'prog-ex4',
title: 'Programmieren: Chatbot-Wortvergleich',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie die C-Funktion `wortvergleich(char wort_1[], char wort_2[])` mit Rückgabetyp `int`. Die Funktion vergleicht die beiden Wörter und liefert 1 für gleiche Wörter, sonst 0. Verwenden Sie keine `string.h`-Funktionen.',
solution: 'int wortvergleich(char wort_1[], char wort_2[]) {\n int i = 0;\n while (wort_1[i] != \'\\0\' && wort_2[i] != \'\\0\') {\n if (wort_1[i] != wort_2[i]) {\n return 0; // Mismatch found\n }\n i++;\n }\n // If we are here, one string has ended.\n // They are equal only if both ended at the same time.\n if (wort_1[i] == \'\\0\' && wort_2[i] == \'\\0\') {\n return 1;\n } \n return 0; // Different lengths\n}',
points: 9,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie die Funktion `wortholen(char satz[])`, die aus einem Satz ein Wort extrahiert und einen `char`-Pointer zurückgibt. Eine statische `int`-Variable `pos` merkt sich die Position. Bei jedem Aufruf wird das nächste Wort zurückgegeben. `wortholen("\\0")` setzt die Position zurück.',
solution: 'char* wortholen(char satz[]) {\n static int pos = 0;\n\n if (satz[0] == \'\\0\') { // Reset condition\n pos = 0;\n return "\\0";\n }\n \n char* wort = &satz[pos];\n if (wort[0] == \'\\0\') { // End of sentence\n pos = 0;\n return "\\0";\n }\n\n while(satz[pos] != \' \' && satz[pos] != \'\\n\' && satz[pos] != \'\\0\') {\n pos++;\n }\n\n if (satz[pos] != \'\\0\') {\n satz[pos] = \'\\0\'; // Terminate the word\n pos++;\n }\n \n return wort;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie die `main()`-Funktion, die einen Satz einliest. Verwenden Sie eine `do-while`-Schleife und `wortholen()`, um den Satz Wort für Wort zu durchlaufen. Vergleichen Sie jedes Wort mit einer vordefinierten `wortliste` via `wortvergleich()`. Bei einer Übereinstimmung, geben Sie die passende Antwort aus `antworten` aus.',
solution: '#include <stdio.h>\n#define WORTLAENGE 20\n#define SATZLAENGE 100\n#define ANZAHL 5\n\nchar wortliste[ANZAHL][WORTLAENGE] = {\n"nicht", "du", "denke", "hasse", "was"};\nchar antworten[ANZAHL][SATZLAENGE] = {\n"sprich am besten davon!",\n"wir sprechen nicht von mir!",\n"warum denkst du das?",\n"du bist sauer - sag mehr dazu!",\n"warum fragst du?"};\n\nint wortvergleich(char wort_1[], char wort_2[]);\nchar* wortholen(char satz[]);\n\nint main() {\n char satz[SATZLAENGE];\n char* wort;\n int gefunden, i;\n\n printf("Hallo wie geht es dir heute?\\n");\n while(fgets(satz, SATZLAENGE, stdin) != NULL) {\n wortholen("\\0"); // Reset word extractor for new sentence\n gefunden = 0;\n do {\n wort = wortholen(satz);\n if (wort[0] == \'\\0\') break;\n\n for (i = 0; i < ANZAHL; i++) {\n if (wortvergleich(wort, wortliste[i])) {\n printf("-> %s\\n", antworten[i]);\n gefunden = 1;\n break;\n }\n }\n if(gefunden) break; // Exit do-while if answer is found\n\n } while (wort[0] != \'\\0\');\n\n if (!gefunden) {\n printf("-> Erzähl mir mehr.\\n");\n }\n printf("\\n");\n }\n return 0;\n}',
points: 7,
type: 'main-function'
},
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert einen einfachen Chatbot, der auf Schlüsselwörter in einem Satz reagiert.\n\n**Teil a)**\n```c\nint wortvergleich(char wort_1[], char wort_2[]) {\n int i = 0;\n // Laufe solange, wie beide Strings Zeichen haben\n while (wort_1[i] != \'\\0\' && wort_2[i] != \'\\0\') {\n if (wort_1[i] != wort_2[i]) {\n return 0; // Zeichen ungleich -> Wörter ungleich\n }\n i++;\n }\n // Wenn Schleife endet, sind die Wörter nur gleich,\n // wenn beide Strings an der gleichen Stelle enden.\n if (wort_1[i] == \'\\0\' && wort_2[i] == \'\\0\') {\n return 1;\n } \n return 0; // Längen sind unterschiedlich\n}\n```\n`wortvergleich` ist eine manuelle Implementierung von `strcmp`. Sie vergleicht zwei Strings Zeichen für Zeichen und stellt sicher, dass sie auch die gleiche Länge haben.\n\n**Teil b)**\n```c\nchar* wortholen(char satz[]) {\n static int pos = 0;\n\n if (satz[0] == \'\\0\') { // Reset-Bedingung\n pos = 0;\n return "\\0";\n }\n \n char* wort = &satz[pos];\n if (wort[0] == \'\\0\') { // Ende des Satzes erreicht\n pos = 0;\n return "\\0";\n }\n\n // Gehe zum Ende des aktuellen Wortes\n while(satz[pos] != \' \' && satz[pos] != \'\\n\' && satz[pos] != \'\\0\') {\n pos++;\n }\n\n // Wenn wir nicht am Ende des Satzes sind, Wort terminieren\n if (satz[pos] != \'\\0\') {\n satz[pos] = \'\\0\';\n pos++; // Gehe zum Anfang des nächsten Wortes\n }\n \n return wort; // Gib den Pointer auf den Wortanfang zurück\n}\n```\n`wortholen` ist ein "Tokenizer". Die `static`-Variable `pos` ist entscheidend, da sie ihren Wert zwischen Funktionsaufrufen behält und sich so die Position im Satz "merkt". Die Funktion modifiziert den Eingabestring, indem sie `\\0`-Zeichen setzt, um die extrahierten Wörter zu terminieren.\n\n**Teil c)**\n```c\n// Globale Variablen und Prototypen (wie in Aufgabe definiert)\n// ...\n\nint main() {\n char satz[SATZLAENGE];\n char* wort;\n int gefunden;\n\n printf("Hallo wie geht es dir heute?\\n");\n // Endlosschleife für den Chat\n while(fgets(satz, SATZLAENGE, stdin) != NULL) {\n wortholen("\\0"); // Position für neuen Satz zurücksetzen\n gefunden = 0;\n do {\n wort = wortholen(satz);\n if (wort[0] == \'\\0\') break; // Ende des Satzes\n\n for (int i = 0; i < ANZAHL; i++) {\n if (wortvergleich(wort, wortliste[i])) {\n printf("-> %s\\n", antworten[i]);\n gefunden = 1;\n break; // innere Schleife verlassen\n }\n }\n if(gefunden) break; // äußere Schleife verlassen\n\n } while (1); // Endlosschleife, wird durch breaks beendet\n\n if (!gefunden) {\n printf("-> Erzähl mir mehr.\\n");\n }\n }\n return 0;\n}\n```\nDie `main`-Funktion enthält die Hauptschleife. Pro Satz wird eine `do-while`-Schleife gestartet, die den Satz mit `wortholen` zerlegt und mit `wortvergleich` nach bekannten Wörtern sucht. Wenn ein Wort gefunden wird, werden beide Schleifen verlassen, um doppelte Antworten zu vermeiden.'
},
{
id: 'prog-ex5',
title: 'Programmieren: Caesar-Verschlüsselung',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `rotiere_buchstabe(char c, int shift)`, die einen Buchstaben `c` um `shift` Stellen im Alphabet verschiebt. Die Funktion soll nur auf Kleinbuchstaben (`a`-`z`) reagieren und am Ende des Alphabets wieder bei `a` anfangen (z.B. `z` mit `shift=2` wird zu `b`). Andere Zeichen sollen unverändert bleiben.',
solution: 'char rotiere_buchstabe(char c, int shift) {\n if (c >= \'a\' && c <= \'z\') {\n return \'a\' + (c - \'a\' + shift) % 26;\n }\n return c;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `verschluessle(char text[], int shift)`, die einen ganzen Text verschlüsselt, indem sie `rotiere_buchstabe()` auf jedes Zeichen des Textes anwendet.',
solution: 'void verschluessle(char text[], int shift) {\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = rotiere_buchstabe(text[i], shift);\n }\n}',
points: 6,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die einen Text und einen `shift`-Wert vom Benutzer einliest, die `verschluessle()`-Funktion aufruft und das Ergebnis ausgibt.',
solution: '#include <stdio.h>\n\nchar rotiere_buchstabe(char c, int shift);\nvoid verschluessle(char text[], int shift);\n\nint main() {\n char text[100];\n int shift;\n\n printf("Text zur Verschluesselung eingeben: ");\n fgets(text, 100, stdin);\n\n printf("Verschiebung eingeben (1-25): ");\n scanf("%d", &shift);\n\n verschluessle(text, shift);\n\n printf("Verschluesselter Text: %s", text);\n\n return 0;\n}',
points: 10,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert die Caesar-Verschlüsselung, eine einfache Form der Zeichen-Substitution.\n\n**Teil a)**\n```c\nchar rotiere_buchstabe(char c, int shift) {\n // Prüfen, ob das Zeichen ein Kleinbuchstabe ist.\n if (c >= \'a\' && c <= \'z\') {\n // Den Buchstaben auf einen Wert 0-25 bringen (a=0, b=1, ...)\n // Shift addieren und Modulo 26 rechnen für den Überlauf.\n // Wieder \'a\' addieren, um zurück in den ASCII-Bereich zu kommen.\n return \'a\' + (c - \'a\' + shift) % 26;\n }\n // Andere Zeichen unverändert zurückgeben.\n return c;\n}\n```\nDie Kernlogik liegt in der Modulo-Arithmetik. `(c - \'a\')` normalisiert den Buchstaben auf einen Index von 0-25. Nach Addition des `shift`-Wertes sorgt `% 26` dafür, dass das Ergebnis im Bereich bleibt und bei `z` wieder zu `a` umbricht.\n\n**Teil b)**\n```c\nvoid verschluessle(char text[], int shift) {\n // Iteriere durch den String bis zum Nullterminator.\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = rotiere_buchstabe(text[i], shift);\n }\n}\n```\nDiese Funktion wendet die Logik aus Teil a) auf einen kompletten String an. Sie modifiziert den String direkt (in-place), da Arrays in C als Referenz übergeben werden.\n\n**Teil c)**\n```c\n#include <stdio.h>\n\n// Prototypen\nchar rotiere_buchstabe(char c, int shift);\nvoid verschluessle(char text[], int shift);\n\nint main() {\n char text[100];\n int shift;\n\n printf("Text eingeben: ");\n fgets(text, 100, stdin);\n\n printf("Verschiebung: ");\n scanf("%d", &shift);\n\n verschluessle(text, shift);\n printf("Ergebnis: %s", text);\n\n return 0;\n}\n```\nDie `main`-Funktion sammelt die Eingaben vom Benutzer, ruft die Verschlüsselungsfunktion auf und gibt das Resultat aus. Es ist eine einfache Demonstration der entwickelten Funktionen.'
},
{
id: 'prog-ex6',
title: 'Programmieren: Array Sortieren (Bubble Sort)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void tausche(int *a, int *b)`, die die Werte der beiden Ganzzahlen, auf die `a` und `b` zeigen, vertauscht.',
solution: 'void tausche(int *a, int *b) {\n int temp = *a;\n *a = *b;\n *b = temp;\n}',
points: 6,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void bubble_sort(int arr[], int n)`, die ein Array `arr` der Größe `n` mithilfe des Bubble-Sort-Algorithmus aufsteigend sortiert. Verwenden Sie die `tausche()`-Funktion.',
solution: 'void bubble_sort(int arr[], int n) {\n for (int i = 0; i < n - 1; i++) {\n for (int j = 0; j < n - i - 1; j++) {\n if (arr[j] > arr[j + 1]) {\n tausche(&arr[j], &arr[j + 1]);\n }\n }\n }\n}',
points: 10,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die ein unsortiertes Integer-Array initialisiert, es ausgibt, `bubble_sort()` aufruft und das sortierte Array erneut ausgibt.\n\n**Hinweis:** Um die Anzahl der Elemente in einem Array zu ermitteln, können Sie den Ausdruck `sizeof(arr) / sizeof(arr[0])` verwenden.',
solution: '#include <stdio.h>\n\nvoid tausche(int *a, int *b);\nvoid bubble_sort(int arr[], int n);\nvoid print_array(int arr[], int size);\n\nint main() {\n int arr[] = {64, 34, 25, 12, 22, 11, 90};\n int n = sizeof(arr) / sizeof(arr[0]);\n\n printf("Unsortiertes Array:\\n");\n print_array(arr, n);\n\n bubble_sort(arr, n);\n\n printf("\\nSortiertes Array:\\n");\n print_array(arr, n);\n\n return 0;\n}\n\nvoid print_array(int arr[], int size) {\n for (int i = 0; i < size; i++)\n printf("%d ", arr[i]);\n printf("\\n");\n}',
points: 8,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert den Bubble-Sort-Algorithmus, einen einfachen, aber lehrreichen Sortieralgorithmus.\n\n**Teil a)**\n```c\nvoid tausche(int *a, int *b) {\n int temp = *a;\n *a = *b;\n *b = temp;\n}\n```\nDie `tausche`-Funktion benötigt Zeiger (`int *`), um die Werte an den ursprünglichen Speicherorten im Array zu ändern. Eine Übergabe per Wert (`int a, int b`) würde nur Kopien der Werte tauschen, was keinen Effekt auf das Array in `main` hätte. Die `temp`-Variable ist notwendig, um einen der Werte zwischenzuspeichern.\n\n**Teil b)**\n```c\nvoid bubble_sort(int arr[], int n) {\n // Äußere Schleife für die Durchläufe\n for (int i = 0; i < n - 1; i++) {\n // Innere Schleife für die Vergleiche\n // n - i - 1, da die letzten i Elemente bereits sortiert sind\n for (int j = 0; j < n - i - 1; j++) {\n // Wenn das aktuelle Element größer als das nächste ist -> tauschen\n if (arr[j] > arr[j + 1]) {\n tausche(&arr[j], &arr[j + 1]);\n }\n }\n }\n}\n```\nBubble Sort vergleicht benachbarte Elemente und tauscht sie, wenn sie in der falschen Reihenfolge sind. Die äußere Schleife sorgt dafür, dass dieser Vorgang oft genug wiederholt wird, bis das gesamte Array sortiert ist. Die innere Schleife "blubbert" das größte unsortierte Element an die richtige Position.\n\n**Teil c)**\n```c\n// includes und Prototypen...\n\nint main() {\n int arr[] = {64, 34, 25, 12, 22, 11, 90};\n // Array-Größe dynamisch berechnen\n int n = sizeof(arr) / sizeof(arr[0]);\n\n printf("Unsortiertes Array: ");\n // (Ausgabefunktion hier)\n\n bubble_sort(arr, n);\n\n printf("Sortiertes Array: ");\n // (Ausgabefunktion hier)\n\n return 0;\n}\n```\nDie `main`-Funktion demonstriert die Nutzung. `sizeof(arr) / sizeof(arr[0])` ist eine robuste Methode, um die Anzahl der Elemente in einem Array zu bestimmen, unabhängig von dessen deklarierter Größe oder Typ.'
},
{
id: 'prog-ex7',
title: 'Programmieren: Dynamischer Speicher',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `int* erstelle_dyn_array(int anzahl)`, die mithilfe von `malloc()` Speicher für ein Array von `anzahl` Integern reserviert. Die Funktion soll den Zeiger auf den Anfang des Speicherbereichs zurückgeben. Geben Sie eine Fehlermeldung aus und geben Sie `NULL` zurück, wenn `malloc` fehlschlägt.',
solution: '#include <stdlib.h>\n#include <stdio.h>\n\nint* erstelle_dyn_array(int anzahl) {\n int* ptr = (int*) malloc(anzahl * sizeof(int));\n if (ptr == NULL) {\n printf("Fehler: Speicher konnte nicht reserviert werden.\\n");\n }\n return ptr;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `int berechne_summe(int* arr, int anzahl)`, die die Summe aller Elemente in dem übergebenen Array `arr` der Größe `anzahl` berechnet und zurückgibt.',
solution: 'int berechne_summe(int* arr, int anzahl) {\n int summe = 0;\n for (int i = 0; i < anzahl; i++) {\n summe += arr[i];\n }\n return summe;\n}',
points: 6,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion: Fragen Sie den Benutzer nach der Anzahl der zu speichernden Zahlen. Rufen Sie `erstelle_dyn_array()` auf. Füllen Sie das Array mit Zahlen (z.B. `i * 2`). Rufen Sie `berechne_summe()` auf und geben Sie das Ergebnis aus. Geben Sie am Ende den Speicher mit `free()` wieder frei.',
solution: '#include <stdio.h>\n#include <stdlib.h>\n\nint* erstelle_dyn_array(int anzahl);\nint berechne_summe(int* arr, int anzahl);\n\nint main() {\n int anzahl;\n printf("Wie viele Zahlen moechten Sie speichern? ");\n scanf("%d", &anzahl);\n\n int* mein_array = erstelle_dyn_array(anzahl);\n\n if (mein_array == NULL) {\n return 1; // Programm mit Fehlercode beenden\n }\n\n printf("Fuelle Array...\\n");\n for (int i = 0; i < anzahl; i++) {\n mein_array[i] = i * 2;\n }\n\n int summe = berechne_summe(mein_array, anzahl);\n printf("Die Summe der Elemente ist: %d\\n", summe);\n\n free(mein_array);\n mein_array = NULL; // Good practice\n\n return 0;\n}',
points: 10,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm zeigt die Grundlagen der dynamischen Speicherverwaltung in C, ein wesentliches Konzept für flexible Programme.\n\n**Teil a)**\n```c\n#include <stdlib.h> // für malloc\n#include <stdio.h> // für printf\n\nint* erstelle_dyn_array(int anzahl) {\n // Speicher anfordern: anzahl * Größe eines Integers\n int* ptr = (int*) malloc(anzahl * sizeof(int));\n // Prüfen, ob malloc erfolgreich war\n if (ptr == NULL) {\n printf("Fehler: Speicher konnte nicht reserviert werden.\\n");\n }\n return ptr;\n}\n```\n`malloc` (memory allocation) reserviert einen Speicherblock einer bestimmten Größe in Bytes und gibt einen `void`-Zeiger darauf zurück. `anzahl * sizeof(int)` berechnet die benötigte Größe. Der `(int*)`-Cast wandelt den `void*` in den korrekten Zeigertyp um. Es ist essenziell, das Ergebnis von `malloc` auf `NULL` zu prüfen, da die Speicheranforderung fehlschlagen kann.\n\n**Teil b)**\n```c\nint berechne_summe(int* arr, int anzahl) {\n int summe = 0;\n for (int i = 0; i < anzahl; i++) {\n summe += arr[i]; // oder: summe += *(arr + i);\n }\n return summe;\n}\n```\nDiese Funktion ist eine Standard-Array-Verarbeitung. Sie zeigt, dass ein dynamisch alloziertes Array genau wie ein statisches Array mit der `[]`-Notation oder Zeigerarithmetik durchlaufen werden kann.\n\n**Teil c)**\n```c\n// includes und Prototypen...\n\nint main() {\n // ... Benutzereingabe ...\n int* mein_array = erstelle_dyn_array(anzahl);\n\n if (mein_array == NULL) {\n return 1; // Fehlerfall behandeln\n }\n\n // ... Array füllen und Summe berechnen ...\n\n // Speicher freigeben, wenn er nicht mehr benötigt wird\n free(mein_array);\n // Zeiger auf NULL setzen, um "dangling pointer" zu vermeiden\n mein_array = NULL; \n\n return 0;\n}\n```\nDie `main`-Funktion orchestriert den Prozess. Der wichtigste Teil ist der Aufruf von `free(mein_array)` am Ende. `free()` gibt den von `malloc` reservierten Speicher wieder frei. Wird dies vergessen, entsteht ein "Memory Leak" das Programm belegt Speicher, den es nicht mehr freigibt. Den Zeiger danach auf `NULL` zu setzen, ist eine gute Praxis, um zu verhindern, dass man versehentlich auf den bereits freigegebenen Speicher zugreift.'
},
{
id: 'prog-ex8',
title: 'Programmieren: Datei-IO (Punkte-Logger)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void schreibe_punktzahl(char* dateiname, char* spieler, int punkte)`, die den Namen des Spielers und seine Punktzahl in die angegebene Datei schreibt. Die Einträge sollen angehängt werden, nicht überschrieben. Format: `Spieler: [Name], Punkte: [Punkte]\\n`.\n\n**Hinweis:** Verwenden Sie `fopen(dateiname, "a")` um die Datei zum Anhängen zu öffnen (`a` für append). Schreiben Sie formatiert mit `fprintf()`. Vergessen Sie nicht, die Datei mit `fclose()` zu schließen.',
solution: '#include <stdio.h>\n\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte) {\n FILE *datei = fopen(dateiname, "a"); // "a" für append (anhängen)\n if (datei == NULL) {\n printf("Fehler beim Oeffnen der Datei zum Schreiben.\\n");\n return;\n }\n fprintf(datei, "Spieler: %s, Punkte: %d\\n", spieler, punkte);\n fclose(datei);\n}',
points: 9,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void lese_punktzahlen(char* dateiname)`, die den gesamten Inhalt der angegebenen Datei zeilenweise liest und auf der Konsole ausgibt.\n\n**Hinweis:** Öffnen Sie die Datei mit `fopen(dateiname, "r")` zum Lesen (`r` für read). Prüfen Sie, ob der zurückgegebene `FILE*`-Zeiger `NULL` ist (Fehlerfall). Lesen Sie zeilenweise mit `fgets()` in einer Schleife, bis es `NULL` zurückgibt (Dateiende). Schließen Sie die Datei am Ende mit `fclose()`.',
solution: '#include <stdio.h>\n\nvoid lese_punktzahlen(char* dateiname) {\n char zeile[100];\n FILE *datei = fopen(dateiname, "r"); // "r" für read (lesen)\n if (datei == NULL) {\n printf("Datei nicht gefunden oder leer.\\n");\n return;\n }\n printf("\\n--- Highscores ---\\n");\n while (fgets(zeile, sizeof(zeile), datei) != NULL) {\n printf("%s", zeile);\n }\n printf("------------------\\n");\n fclose(datei);\n}',
points: 8,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die `schreibe_punktzahl()` für zwei fiktive Spieler aufruft (z.B. "Alice" mit 150 Pkt, "Bob" mit 120 Pkt). Rufen Sie anschließend `lese_punktzahlen()` auf, um das Ergebnis zu überprüfen.',
solution: '#include <stdio.h>\n\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte);\nvoid lese_punktzahlen(char* dateiname);\n\nint main() {\n char* dateiname = "scores.txt";\n\n // Alte Datei ggf. löschen für einen sauberen Testlauf\n remove(dateiname);\n\n printf("Schreibe Punktzahlen in die Datei...\\n");\n schreibe_punktzahl(dateiname, "Alice", 150);\n schreibe_punktzahl(dateiname, "Bob", 120);\n\n lese_punktzahlen(dateiname);\n\n return 0;\n}',
points: 7,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm demonstriert die grundlegende Dateiein- und -ausgabe (File I/O) in C, um Daten persistent zu speichern.\n\n**Teil a)**\n```c\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte) {\n // Datei im "append"-Modus öffnen. Erstellt die Datei, wenn sie nicht existiert.\n FILE *datei = fopen(dateiname, "a");\n if (datei == NULL) {\n // Fehlerbehandlung ist wichtig!\n printf("Fehler beim Oeffnen der Datei zum Schreiben.\\n");\n return;\n }\n // fprintf funktioniert wie printf, schreibt aber in eine Datei.\n fprintf(datei, "Spieler: %s, Punkte: %d\\n", spieler, punkte);\n // Datei schließen, um Änderungen zu speichern und Ressourcen freizugeben.\n fclose(datei);\n}\n```\n`fopen` öffnet eine Datei und gibt einen `FILE`-Zeiger zurück. Der Modus `"a"` (append) ist entscheidend, um neue Einträge am Ende der Datei hinzuzufügen, anstatt sie zu überschreiben (Modus `"w"`). `fprintf` wird für formatiertes Schreiben in die Datei verwendet. `fclose` ist obligatorisch.\n\n**Teil b)**\n```c\nvoid lese_punktzahlen(char* dateiname) {\n char zeile[100];\n // Datei im "read"-Modus öffnen.\n FILE *datei = fopen(dateiname, "r"); \n if (datei == NULL) {\n printf("Datei nicht gefunden oder leer.\\n");\n return;\n }\n printf("\\n--- Highscores ---\\n");\n // Lese zeilenweise, bis das Ende der Datei erreicht ist (fgets gibt NULL zurück).\n while (fgets(zeile, sizeof(zeile), datei) != NULL) {\n printf("%s", zeile);\n }\n printf("------------------\\n");\n fclose(datei);\n}\n```\nHier wird `fopen` im Modus `"r"` (read) verwendet. `fgets` ist eine sichere Methode, um zeilenweise aus einer Datei zu lesen, da es die Puffergröße beachtet und so Buffer Overflows verhindert. Die Schleife liest so lange, bis `fgets` das Dateiende erreicht und `NULL` zurückgibt.\n\n**Teil c)**\n```c\nint main() {\n char* dateiname = "scores.txt";\n\n // Optional: remove() löscht eine Datei. Nützlich für wiederholbare Tests.\n remove(dateiname);\n\n schreibe_punktzahl(dateiname, "Alice", 150);\n schreibe_punktzahl(dateiname, "Bob", 120);\n\n lese_punktzahlen(dateiname);\n\n return 0;\n}\n```\nDie `main`-Funktion dient als Treiber, um die beiden I/O-Funktionen zu testen und deren Zusammenspiel zu demonstrieren.'
},
{
id: 'prog-ex9',
title: 'Programmieren: Rechteck-Struktur & Funktionen',
parts: [
{
id: 'a',
prompt: 'Definieren Sie mittels `typedef` einen Datentyp `RECHTECK` für eine Struktur, die `breite` und `hoehe` (beides `float`) enthält.',
solution: 'typedef struct {\n float breite;\n float hoehe;\n} RECHTECK;',
points: 6,
type: 'typedef-struct'
},
{
id: 'b',
prompt: 'Erstellen Sie eine Funktion `float berechne_flaeche(RECHTECK *r)`, die die Fläche eines Rechtecks berechnet. Die Funktion soll einen Zeiger auf eine `RECHTECK`-Struktur als Parameter erhalten.',
solution: 'float berechne_flaeche(RECHTECK *r) {\n return r->breite * r->hoehe;\n}',
points: 9,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Schreiben Sie eine `main`-Funktion, die eine `RECHTECK`-Variable deklariert und initialisiert. Rufen Sie dann `berechne_flaeche` auf, um die Fläche zu berechnen und geben Sie das Ergebnis auf der Konsole aus.',
solution: '#include <stdio.h>\n\ntypedef struct {\n float breite;\n float hoehe;\n} RECHTECK;\n\nfloat berechne_flaeche(RECHTECK *r);\n\nint main() {\n RECHTECK mein_rechteck = {10.5, 5.2};\n\n float flaeche = berechne_flaeche(&mein_rechteck);\n\n printf("Das Rechteck mit Breite %.2f und Hoehe %.2f hat eine Flaeche von %.2f.\\n", mein_rechteck.breite, mein_rechteck.hoehe, flaeche);\n\n return 0;\n}',
points: 9,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDiese Aufgabe verdeutlicht das Zusammenspiel von Strukturen (`struct`) und Funktionen, insbesondere die effiziente Übergabe von Strukturen mittels Zeigern.\n\n**Teil a)**\n```c\ntypedef struct {\n float breite;\n float hoehe;\n} RECHTECK;\n```\nEin `typedef` wird verwendet, um einen Alias `RECHTECK` für die anonyme Struktur zu erstellen. Dies macht den Code lesbarer, da man nicht immer `struct ...` schreiben muss.\n\n**Teil b)**\n```c\nfloat berechne_flaeche(RECHTECK *r) {\n return r->breite * r->hoehe;\n}\n```\nDiese Funktion nimmt einen Zeiger auf ein `RECHTECK` entgegen (`RECHTECK *r`). Dies ist weitaus effizienter als die Übergabe der ganzen Struktur per Wert (`RECHTECK r`), besonders bei großen Strukturen. Bei der Übergabe per Zeiger wird nur die Speicheradresse (typ. 4 oder 8 Bytes) kopiert, nicht die gesamte Struktur. Der `->`-Operator (Pfeiloperator) wird verwendet, um auf Strukturelemente über einen Zeiger zuzugreifen. `r->breite` ist eine Kurzschreibweise für `(*r).breite`.\n\n**Teil c)**\n```c\nint main() {\n RECHTECK mein_rechteck = {10.5, 5.2};\n\n // Die Adresse der Struktur wird mit dem &-Operator übergeben.\n float flaeche = berechne_flaeche(&mein_rechteck);\n\n printf("Die Flaeche betraegt: %.2f\\n", flaeche);\n\n return 0;\n}\n```\nIn `main` wird eine Instanz der Struktur erstellt und initialisiert. Beim Aufruf von `berechne_flaeche` wird der Adressoperator `&` verwendet, um einen Zeiger auf `mein_rechteck` zu erzeugen, der dann an die Funktion übergeben wird. Dies entspricht dem von der Funktion erwarteten Parametertyp `RECHTECK*`.'
},
{
id: 'prog-ex10',
title: 'Programmieren: Matrix-Transponierung',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void print_matrix(int matrix[3][3])`, die eine 3x3-Matrix formatiert auf der Konsole ausgibt.',
solution: '#include <stdio.h>\n\nvoid print_matrix(int matrix[3][3]) {\n for (int i = 0; i < 3; i++) {\n for (int j = 0; j < 3; j++) {\n printf("%d\\t", matrix[i][j]);\n }\n printf("\\n");\n }\n}',
points: 7,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void transponiere_matrix(int matrix[3][3])`, die die übergebene 3x3-Matrix an Ort und Stelle ("in-place") transponiert. Dabei werden die Zeilen und Spalten vertauscht (Element an `(i,j)` wird mit Element an `(j,i)` getauscht).',
solution: 'void transponiere_matrix(int matrix[3][3]) {\n int temp;\n for (int i = 0; i < 3; i++) {\n for (int j = i + 1; j < 3; j++) {\n temp = matrix[i][j];\n matrix[i][j] = matrix[j][i];\n matrix[j][i] = temp;\n }\n }\n}',
points: 9,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die eine 3x3-Matrix initialisiert. Geben Sie die Originalmatrix mit `print_matrix()` aus, rufen Sie `transponiere_matrix()` auf und geben Sie die transponierte Matrix erneut aus.',
solution: '#include <stdio.h>\n\nvoid print_matrix(int matrix[3][3]);\nvoid transponiere_matrix(int matrix[3][3]);\n\nint main() {\n int matrix[3][3] = {\n {1, 2, 3},\n {4, 5, 6},\n {7, 8, 9}\n };\n\n printf("Originalmatrix:\\n");\n print_matrix(matrix);\n\n transponiere_matrix(matrix);\n\n printf("\\nTransponierte Matrix:\\n");\n print_matrix(matrix);\n\n return 0;\n}',
points: 8,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDiese Übung konzentriert sich auf den Umgang mit zweidimensionalen Arrays (Matrizen) und die Implementierung eines Matrix-Algorithmus.\n\n**Teil a)**\n```c\nvoid print_matrix(int matrix[3][3]) {\n // Äußere Schleife für die Zeilen\n for (int i = 0; i < 3; i++) {\n // Innere Schleife für die Spalten\n for (int j = 0; j < 3; j++) {\n printf("%d\\t", matrix[i][j]); // \\t für Tabulator -> saubere Spalten\n }\n printf("\\n"); // Neue Zeile nach jeder Matrix-Zeile\n }\n}\n```\nDas Ausgeben einer Matrix erfordert zwei verschachtelte Schleifen. Die äußere iteriert über die Zeilen (erster Index), die innere über die Spalten (zweiter Index). Der Zugriff auf ein Element erfolgt mit `matrix[zeile][spalte]`.\n\n**Teil b)**\n```c\nvoid transponiere_matrix(int matrix[3][3]) {\n int temp;\n // Iteriere über das obere Dreieck der Matrix\n for (int i = 0; i < 3; i++) {\n for (int j = i + 1; j < 3; j++) {\n // Tausche element(i,j) mit element(j,i)\n temp = matrix[i][j];\n matrix[i][j] = matrix[j][i];\n matrix[j][i] = temp;\n }\n }\n}\n```\nBeim Transponieren werden die Elemente über die Hauptdiagonale gespiegelt. Um dies "in-place" zu tun, müssen wir nur die Elemente im oberen (oder unteren) Dreieck der Matrix durchlaufen und sie mit ihrem Gegenstück tauschen. Die innere Schleife startet bei `j = i + 1`, um zu vermeiden, dass Elemente auf der Diagonale mit sich selbst und andere Elemente doppelt getauscht werden (was die Transponierung rückgängig machen würde).\n\n**Teil c)**\n```c\nint main() {\n int matrix[3][3] = {\n {1, 2, 3},\n {4, 5, 6},\n {7, 8, 9}\n };\n\n printf("Originalmatrix:\\n");\n print_matrix(matrix);\n\n transponiere_matrix(matrix);\n\n printf("\\nTransponierte Matrix:\\n");\n print_matrix(matrix);\n\n return 0;\n}\n```\nDie `main`-Funktion initialisiert eine Beispielmatrix und nutzt die zuvor erstellten Funktionen, um den Transponierungs-Vorgang zu demonstrieren und das Ergebnis sichtbar zu machen. Da Arrays in C als Referenz übergeben werden, modifiziert `transponiere_matrix` die Originalmatrix in `main` direkt.'
}
];