diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8f4782a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,40 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug test_stack", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_stack.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + }, + { + "name": "Debug test_numbers", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_numbers.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + }, + { + "name": "Debug test_bintree", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/test_bintree.exe", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerPath": "C:/ProgramData/mingw64/mingw64/bin/gdb.exe", + "preLaunchTask": "Build via Makefile" + } + + + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..dad5165 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build via Makefile", + "type": "shell", + "command": "mingw32-make", + "args": ["${input:target}"], + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$gcc" + } + ], + "inputs": [ + { + "id": "target", + "type": "pickString", + "description": "Welches Makefile-Target soll gebaut werden?", + "options": ["test_stack", "test_numbers", "test_bintree", "doble", "unitTests"] + } + ] +} \ No newline at end of file diff --git a/bintree.c b/bintree.c index 668c164..91f0430 100644 --- a/bintree.c +++ b/bintree.c @@ -1,111 +1,148 @@ + #include #include #include "stack.h" #include "bintree.h" -// internal helper to push node and all its left descendants onto iterator stack -static void bintree_pushLefts(StackNode **iterStackPtr, TreeNode *n) +/* + Schiebt einen Knoten und alle seine linken Nachfolger + (entlang der "linken Kante") auf den Iterator-Stack. + iterStackPtr: Zeiger auf den Top-Zeiger des Stacks (LIFO) für die Wiederholung + knoten: aktueller Startknoten, dessen linke Kette abgelegt wird +*/ +static void bintree_pushLefts(StackNode **iterStackPtr, TreeNode *knoten) { - while(n != NULL) + while (knoten != NULL) { - *iterStackPtr = push(*iterStackPtr, n); - n = n->left; + *iterStackPtr = push(*iterStackPtr, knoten); // aktuellen Knoten oben auf den Stack legen + knoten = knoten->left; // zum linken Kind weiterlaufen } } -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). -TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) +/* + Fügt eine Kopie der Daten (Speicherbereich von 'data' mit Länge 'dataSize') in den Baum ein. + Die Ordnung wird über 'compareFct' festgelegt. + + Duplikate: + - Wenn 'isDuplicate' != NULL übergeben wird: Duplikate werden NICHT eingefügt, + stattdessen wird *isDuplicate = 1 gesetzt (bei neuem Eintrag = 0). + - Wenn 'isDuplicate' == NULL: Duplikate SIND erlaubt; der Duplikat-Eintrag + wird in den rechten Teilbaum eingefügt. + + Rückgabe: + - Zeiger auf die (ggf. unveränderte oder neue) Wurzel des Teilbaums. + - NULL bei Speicherfehlern beim Anlegen des ersten Knotens. +*/ +TreeNode *addToTree(TreeNode *wurzel, + const void *daten, + size_t datenGroesse, + CompareFctType vergleichFkt, + int *istDuplikat) { - if(isDuplicate != NULL) - *isDuplicate = 0; + // Standardmäßig annehmen: kein Duplikat (falls Ausgabefeld vorhanden) + if (istDuplikat != NULL) + *istDuplikat = 0; - if(root == NULL) + // Leerer Baum/Teilbaum: neuen Knoten erzeugen + if (wurzel == NULL) { - TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode)); - if(node == NULL) - return NULL; - node->data = malloc(dataSize); - if(node->data == NULL) + TreeNode *neuerKnoten = (TreeNode *)malloc(sizeof(TreeNode)); + if (neuerKnoten == NULL) + return NULL; // Speicherfehler + + neuerKnoten->data = malloc(datenGroesse); + if (neuerKnoten->data == NULL) { - free(node); - return NULL; + free(neuerKnoten); + return NULL; // Speicherfehler für Datenbereich } - memcpy(node->data, data, dataSize); - node->left = node->right = NULL; - return node; + + memcpy(neuerKnoten->data, daten, datenGroesse); // tiefe Kopie der Nutzdaten + neuerKnoten->left = neuerKnoten->right = NULL; // Blatt + return neuerKnoten; } - int cmp = compareFct(data, root->data); - if(cmp < 0) + // Vergleich der einzufügenden Daten mit dem aktuellen Knoten + int vergleich = vergleichFkt(daten, wurzel->data); + + if (vergleich < 0) { - root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); + // links einfügen + wurzel->left = addToTree(wurzel->left, daten, datenGroesse, vergleichFkt, istDuplikat); } - else if(cmp > 0) + else if (vergleich > 0) { - root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); + // rechts einfügen + wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat); } - else // equal + else { - if(isDuplicate != NULL) + // Gleichheit (potenzielles Duplikat) + if (istDuplikat != NULL) { - *isDuplicate = 1; - // do not insert duplicate + *istDuplikat = 1; // Duplikat erkannt, NICHT einfügen + // keine Änderung am Baum } else { - // duplicates allowed -> insert into right subtree - root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate); + // Duplikate erlaubt -> konventionell in den rechten Teilbaum einfügen + wurzel->right = addToTree(wurzel->right, daten, datenGroesse, vergleichFkt, istDuplikat); } } - return root; + return wurzel; } -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element, -// push the top node and push all its left nodes. -void *nextTreeData(TreeNode *root) +void *nextTreeData(TreeNode *wurzel) { - static StackNode *iterStack = NULL; + static StackNode *iteratorStack = NULL; // interner Zustand über Aufrufe hinweg - // If a new tree root is provided -> reset iterator - if(root != NULL) + // Neuer Baum übergeben -> Iterator zurücksetzen/initialisieren + if (wurzel != NULL) { - clearStack(iterStack); - iterStack = NULL; - bintree_pushLefts(&iterStack, root); + clearStack(iteratorStack); // ggf. alten Stack leeren (Speicher freigeben) + iteratorStack = NULL; // Top-Zeiger zurücksetzen + bintree_pushLefts(&iteratorStack, wurzel); // Wurzel und linke Kette ablegen } - if(iterStack == NULL) + // Kein weiterer Eintrag? + if (iteratorStack == NULL) return NULL; - TreeNode *node = (TreeNode *)top(iterStack); - iterStack = pop(iterStack); + // Nächsten Knoten holen (oberstes Stack-Element) + TreeNode *aktuellerKnoten = (TreeNode *)top(iteratorStack); + iteratorStack = pop(iteratorStack); - if(node->right != NULL) - bintree_pushLefts(&iterStack, node->right); + // Falls rechter Teilbaum existiert: dessen linke Kette ablegen + if (aktuellerKnoten->right != NULL) + bintree_pushLefts(&iteratorStack, aktuellerKnoten->right); - return node->data; + // Daten des aktuellen Knotens zurückgeben + return aktuellerKnoten->data; } -// Releases all memory resources (including data copies). -void clearTree(TreeNode *root) +/* + Gibt den gesamten Baum frei (inkl. der tief kopierten Daten). +*/ +void clearTree(TreeNode *wurzel) { - if(root == NULL) + if (wurzel == NULL) return; - clearTree(root->left); - clearTree(root->right); + clearTree(wurzel->left); + clearTree(wurzel->right); - free(root->data); - free(root); + free(wurzel->data); + free(wurzel); } -// Returns the number of entries in the tree given by root. -unsigned int treeSize(const TreeNode *root) +/* + Liefert die Anzahl der Knoten/Einträge im Teilbaum 'wurzel'. +*/ +unsigned int treeSize(const TreeNode *wurzel) { - if(root == NULL) + if (wurzel == NULL) return 0; - return 1 + treeSize(root->left) + treeSize(root->right); -} \ No newline at end of file + + return 1U + treeSize(wurzel->left) + treeSize(wurzel->right); +} diff --git a/bintree.h b/bintree.h index 25e16b2..7ea1ff1 100644 --- a/bintree.h +++ b/bintree.h @@ -1,27 +1,81 @@ + #ifndef BINTREE_H #define BINTREE_H #include +/* + Typdefinition für die Vergleichsfunktion. + + Die Funktion muss zwei Datenzeiger vergleichen und zurückgeben: + - < 0, wenn arg1 kleiner als arg2 ist + - 0, wenn arg1 gleich arg2 ist + - > 0, wenn arg1 größer als arg2 ist +*/ typedef int (*CompareFctType)(const void *arg1, const void *arg2); +/* + Struktur für einen Binärbaum-Knoten. + + Enthält: + - data: Zeiger auf die gespeicherten Daten (beliebiger Typ, dynamisch allokiert) + - left: Zeiger auf linken Teilbaum + - right: Zeiger auf rechten Teilbaum +*/ typedef struct node { - void *data; - struct node *left; - struct node *right; + void *data; // Zeiger auf die Nutzdaten + struct node *left; // Zeiger auf linken Kindknoten + struct node *right; // Zeiger auf rechten Kindknoten } TreeNode; -// Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). +/* + Fügt eine Kopie der Daten in den Binärbaum ein. + + Parameter: + root : Wurzel des (Teil-)Baums + data : Zeiger auf die einzufügenden Daten + dataSize : Größe der Daten in Bytes + compareFct : Vergleichsfunktion für die Ordnung + isDuplicate : Optionaler Zeiger: + - Wenn NULL: Duplikate sind erlaubt + - Wenn != NULL: Duplikate werden ignoriert und *isDuplicate wird gesetzt: + - 0: neuer Eintrag eingefügt + - 1: Duplikat erkannt, nicht eingefügt + + Rückgabe: + Zeiger auf die (ggf. neue) Wurzel des Baums oder NULL bei Speicherfehler. +*/ TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate); -// Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction. -// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element, -// push the top node and push all its left nodes. + +/* + Iteriert über den Baum in Inorder-Reihenfolge. + + Verhalten: + - Erster Aufruf mit root != NULL: Iterator initialisieren + - Folgeaufrufe mit root == NULL: nächstes Element zurückgeben + + Rückgabe: + Zeiger auf die Daten des nächsten Knotens oder NULL, wenn Ende erreicht. + + Hinweis: + Intern wird ein Stack verwendet. Nicht threadsicher. +*/ void *nextTreeData(TreeNode *root); -// Releases all memory resources (including data copies). + +/* + Gibt den gesamten Baum frei (inklusive der gespeicherten Daten). + + Nach dem Aufruf sind alle Zeiger ungültig. +*/ void clearTree(TreeNode *root); -// Returns the number of entries in the tree given by root. + +/* + Liefert die Anzahl der Knoten im Baum. + + Rückgabe: + Anzahl der Knoten (0 bei leerem Baum). +*/ unsigned int treeSize(const TreeNode *root); -#endif \ No newline at end of file +#endif diff --git a/bintree.o b/bintree.o deleted file mode 100644 index 47986c5..0000000 Binary files a/bintree.o and /dev/null differ diff --git a/doble b/doble deleted file mode 100755 index 88b0bc9..0000000 Binary files a/doble and /dev/null differ diff --git a/doble.exe b/doble.exe new file mode 100644 index 0000000..04ae889 Binary files /dev/null and b/doble.exe differ diff --git a/doble_initial b/doble_initial deleted file mode 100755 index 69c6a2e..0000000 Binary files a/doble_initial and /dev/null differ diff --git a/doble_initial.exe b/doble_initial.exe new file mode 100644 index 0000000..a91c009 Binary files /dev/null and b/doble_initial.exe differ diff --git a/highscore.o b/highscore.o deleted file mode 100644 index b268078..0000000 Binary files a/highscore.o and /dev/null differ diff --git a/highscores.txt b/highscores.txt index 7f7e944..cda4174 100644 --- a/highscores.txt +++ b/highscores.txt @@ -1,10 +1,10 @@ +Player1;19887 +Player1;19843 Lena;19811 Lena;19702 +Player1;19578 player_name;9981 Lena;9980 Lena;9978 Lena;9978 Lena;9976 -Lena;9975 -Lena;9971 -Lena;9965 diff --git a/main.o b/main.o deleted file mode 100644 index f167660..0000000 Binary files a/main.o and /dev/null differ diff --git a/numbers.c b/numbers.c index f45d131..319f4c3 100644 --- a/numbers.c +++ b/numbers.c @@ -1,3 +1,4 @@ + #include #include #include @@ -5,70 +6,135 @@ #include "numbers.h" #include "bintree.h" -// helper comparator for unsigned int for bintree +/** + * @brief Vergleichsfunktion für unsigned int-Werte zur Verwendung im Binärbaum. + * + * Diese Funktion wird von der Binärbaum-Implementierung genutzt, um die + * Ordnung der Knoten zu bestimmen. Sie vergleicht die dereferenzierten + * unsigned int-Werte a und b. + * + * @param a Pointer auf einen unsigned int-Wert (linker Operand) + * @param b Pointer auf einen unsigned int-Wert (rechter Operand) + * @return -1, falls *a < *b; 1, falls *a > *b; 0, falls *a == *b + */ static int compareUInt(const void *a, const void *b) { unsigned int va = *(const unsigned int *)a; unsigned int vb = *(const unsigned int *)b; - if(va < vb) return -1; - if(va > vb) return 1; + if (va < vb) return -1; + if (va > vb) return 1; return 0; } -// comparator for qsort (unsigned int) +/** + * @brief Vergleichsfunktion für qsort() zur Sortierung von unsigned int-Arrays. + * + * @param a Pointer auf einen Arrayeintrag + * @param b Pointer auf einen Arrayeintrag + * @return -1, 0, 1 analog zu compareUInt() + */ static int qsort_uint_cmp(const void *a, const void *b) { unsigned int va = *(const unsigned int *)a; unsigned int vb = *(const unsigned int *)b; - if(va < vb) return -1; - if(va > vb) return 1; + if (va < vb) return -1; + if (va > vb) return 1; return 0; } -// Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries. -// Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while -// creating random numbers. +/** + * @brief Erzeugt ein Array aus len Zufallszahlen im Bereich [1 .. 2*len], + * das genau einen duplizierten Wert enthält (d. h. len-1 eindeutige + 1 Duplikat), + * und mischt die Reihenfolge zufällig. + * + * Funktionsweise: + * - Es werden zunächst len-1 eindeutige Zufallszahlen erzeugt. Die Eindeutigkeit wird + * mithilfe eines Binärsuchbaums (BST) geprüft: addToTree() fügt die Zahl ein + * und signalisiert per isDup, ob sie bereits vorhanden war. + * - Anschließend wird eine der bereits erzeugten Zahlen zufällig ausgewählt und + * noch einmal an das Ende des Arrays geschrieben, um das geforderte Duplikat sicherzustellen. + * - Zum Schluss wird das gesamte Array mittels Fisher–Yates-Algorithmus gemischt. + * + * Fehlerbehandlung: + * - Bei len < 2 wird NULL zurückgegeben, da das Problem ein Duplikat erfordert. + * - Bei Speicher- oder Baum-Insertionsfehlern wird aufgeräumt und NULL zurückgegeben. + * Wichtig: Der Baumzeiger root wird erst nach erfolgreichem Insert aktualisiert, + * um im Fehlerfall kein bereits aufgebautes Teilbaum-Objekt zu verlieren. + * + * Randbedingungen / Annahmen: + * - addToTree(root, &val, sizeof(val), compareUInt, &isDup) setzt isDup: + * isDup == 1 bedeutet „Duplikat gefunden, Baum unverändert“, + * isDup == 0 bedeutet „neuer Wert eingefügt (oder Fehler)“. + * - Bei Speicherfehler gibt addToTree NULL zurück und isDup bleibt 0. + * - clearTree(root) darf mit NULL-Argument aufgerufen werden (No-Op). + * + * Komplexität: + * - Durchschnittlich O(len * log(len)) für die len-1 Einfügungen in den BST. + * - Shuffle in O(len). + * + * @param len Anzahl der zu erzeugenden Werte (muss >= 2 sein) + * @return Pointer auf ein Array mit len Einträgen bei Erfolg; NULL bei Fehlern + */ unsigned int *createNumbers(unsigned int len) { - if(len < 2) + if (len < 2) return NULL; unsigned int *numbers = (unsigned int *)malloc(sizeof(unsigned int) * len); - if(numbers == NULL) + if (numbers == NULL) return NULL; - // seed once - srand((unsigned int)time(NULL)); + // Zufallszahlengenerator nur einmal pro Prozess initialisieren. + // Hintergrund: Wird createNumbers mehrfach schnell hintereinander gerufen, + // kann time(NULL) identische Seeds liefern und damit identische Zahlenfolgen erzeugen. + static int seeded = 0; + if (!seeded) { + srand((unsigned int)time(NULL)); + seeded = 1; + } TreeNode *root = NULL; unsigned int range = 2 * len; - // create len-1 unique numbers - for(unsigned int i = 0; i < len - 1; i++) + + // Schritt 1: len-1 eindeutige Zufallszahlen erzeugen + for (unsigned int i = 0; i < len - 1; i++) { unsigned int val; - int isDup = 0; - // try until a unique number is inserted - do + int isDup; + + // Wiederholen, bis eine wirklich neue Zahl eingefügt wurde + for (;;) { - val = (unsigned int)(rand() % range) + 1; // [1..2*len] - root = addToTree(root, &val, sizeof(val), compareUInt, &isDup); - // if addToTree returned NULL due to allocation failure, cleanup and return NULL - if(root == NULL && isDup == 0) - { + isDup = 0; // vor jedem Insert zurücksetzen, um „alte“ Werte zu vermeiden + val = (unsigned int)(rand() % range) + 1; // Wertebereich [1 .. 2*len] + + // addToTree kann bei Erfolg einen (ggf. neuen) Wurzelzeiger liefern. + // Zur Vermeidung eines Speicherlecks bei Fehlern zunächst in temp speichern. + TreeNode *newRoot = addToTree(root, &val, sizeof(val), compareUInt, &isDup); + + if (newRoot == NULL && isDup == 0) { + // Vermutlich Speicher-/Insertionsfehler: aufräumen und abbrechen free(numbers); - clearTree(root); + clearTree(root); // root zeigt noch auf den gültigen Teilbaum return NULL; } - } while(isDup); - numbers[i] = val; + + if (!isDup) { + // Einfügen war erfolgreich und der Wert ist eindeutig. + root = newRoot; + numbers[i] = val; + break; + } + // Andernfalls Duplikat: Neue Zufallszahl versuchen. + } } - // duplicate one existing random entry - unsigned int idx = (unsigned int)(rand() % (len - 1)); + // Schritt 2: Eine der bestehenden Zahlen zufällig duplizieren + unsigned int idx = (unsigned int)(rand() % (len - 1)); // Index im Bereich [0 .. len-2] numbers[len - 1] = numbers[idx]; - // shuffle array (Fisher-Yates) - for(unsigned int i = len - 1; i > 0; i--) + // Schritt 3: Fisher–Yates-Shuffle über das gesamte Array + for (unsigned int i = len - 1; i > 0; i--) { unsigned int j = (unsigned int)(rand() % (i + 1)); unsigned int tmp = numbers[i]; @@ -76,31 +142,53 @@ unsigned int *createNumbers(unsigned int len) numbers[j] = tmp; } - // free tree resources + // Aufräumen: Baum freigeben clearTree(root); return numbers; } -// Returns only the only number in numbers which is present twice. Returns zero on errors. +/** + * @brief Findet den einzigen duplizierten Wert in einem Array aus len unsigned int. + * + * Funktionsweise: + * - Es wird eine Kopie des Eingabearrays erstellt, um die Reihenfolge des + * Originalarrays nicht zu verändern. + * - Die Kopie wird mittels qsort() aufsteigend sortiert. + * - Beim Durchlauf werden benachbarte Elemente verglichen. Da genau ein Wert + * doppelt vorkommt, finden wir ihn als erstes Paar gleicher Nachbarn. + * + * Fehlerbehandlung: + * - Bei ungültigen Parametern (numbers == NULL oder len < 2) wird 0 geliefert. + * - Bei Speicherfehlern beim Kopieren ebenfalls 0. + * + * Komplexität: + * - Sortieren in O(len * log(len)), anschließender Linearpass O(len). + * + * @param numbers Pointer auf das Eingabearray + * @param len Länge des Arrays (muss >= 2 sein) + * @return Der doppelte Wert; 0 bei Fehlern oder falls kein Duplikat gefunden wurde + * (gemäß Aufgabenstellung sollte aber genau ein Duplikat existieren). + */ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { - if(numbers == NULL || len < 2) + if (numbers == NULL || len < 2) return 0; - // make a copy so original array order is not modified by caller expectation + // Kopie erstellen, damit das Original unangetastet bleibt unsigned int *copy = (unsigned int *)malloc(sizeof(unsigned int) * len); - if(copy == NULL) + if (copy == NULL) return 0; memcpy(copy, numbers, sizeof(unsigned int) * len); + // Sortieren der Kopie qsort(copy, len, sizeof(unsigned int), qsort_uint_cmp); + // Linearer Scan: erstes Paar identischer Nachbarn ist das Duplikat unsigned int result = 0; - for(unsigned int i = 0; i + 1 < len; i++) + for (unsigned int i = 0; i + 1 < len; i++) { - if(copy[i] == copy[i+1]) - { + if (copy[i] == copy[i + 1]) { result = copy[i]; break; } @@ -108,4 +196,4 @@ unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) free(copy); return result; -} \ No newline at end of file +} diff --git a/numbers.o b/numbers.o deleted file mode 100644 index e87e9fe..0000000 Binary files a/numbers.o and /dev/null differ diff --git a/stack.c b/stack.c index 0472a36..0d54b55 100644 --- a/stack.c +++ b/stack.c @@ -1,15 +1,15 @@ #include #include "stack.h" -// Pushes data as pointer onto the stack. +// Push Daten auf den Stack legen. StackNode *push(StackNode *stack, void *data) { - StackNode *node = (StackNode *)malloc(sizeof(StackNode)); + StackNode *node = malloc(sizeof(StackNode)); if(node == NULL) return stack; // allocation failed -> return unchanged stack - node->data = data; - node->next = stack; + node->data = data; // Set the data for the new node + node->next = stack; // New node points to the previous top of the stack return node; } diff --git a/stack.o b/stack.o deleted file mode 100644 index 014f77e..0000000 Binary files a/stack.o and /dev/null differ diff --git a/test_numbers.c b/test_numbers.c index 067bb3b..e14e98b 100644 --- a/test_numbers.c +++ b/test_numbers.c @@ -1,41 +1,91 @@ + #include #include #include #include "numbers.h" +/** + * @brief Selbsttest für createNumbers() und getDuplicate(). + * + * Erzeugt ein Array aus len Zufallszahlen mit genau einem duplizierten Wert, + * validiert die Eigenschaften per Zähl-Array (Wertebereich, Häufigkeiten) + * und prüft anschließend, ob getDuplicate() dasselbe Duplikat ermittelt. + * + * Rückgabecodes: + * 0: OK + * 1: createNumbers() fehlgeschlagen + * 2: Speicherfehler für counts + * 3: Wert außerhalb des Bereichs [1..2*len] + * 4: Ein Wert erscheint öfter als zweimal + * 5: Nicht genau ein Duplikat gefunden + * 6: getDuplicate() liefert anderes Ergebnis als Zählung + */ int main(void) { - unsigned int len = 100; + unsigned int len = 100; // Anzahl zu erzeugender Zahlen unsigned int *nums = createNumbers(len); - if(nums == NULL) { fprintf(stderr, "createNumbers returned NULL\n"); return 1; } - - // count occurrences - unsigned int maxVal = 2 * len; - unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int)); - if(counts == NULL) { free(nums); return 2; } - - for(unsigned int i = 0; i < len; i++) - { - if(nums[i] > maxVal) { fprintf(stderr, "value out of expected range\n"); free(nums); free(counts); return 3; } - counts[nums[i]]++; + if (nums == NULL) { + fprintf(stderr, "createNumbers returned NULL\n"); + return 1; // Erzeugung fehlgeschlagen } + unsigned int maxVal = 2 * len; // Erlaubter Bereich: [1 .. 2*len] + + // Zähl-Array für Häufigkeiten pro Wert (Index 0 bleibt ungenutzt) + unsigned int *counts = calloc(maxVal + 1, sizeof(unsigned int)); + if (counts == NULL) { + free(nums); + return 2; // Speicherfehler bei counts + } + + // Häufigkeiten bestimmen und gleichzeitig Bereich prüfen + for (unsigned int i = 0; i < len; i++) { + unsigned int v = nums[i]; + if (v == 0 || v > maxVal) { // sollte nicht passieren, wenn createNumbers korrekt ist + fprintf(stderr, "value out of expected range\n"); + free(nums); + free(counts); + return 3; + } + counts[v]++; // Auftreten des Werts v zählen + } + + // Exakt ein Wert muss doppelt vorkommen; keiner darf >2-mal vorkommen int duplicatesFound = 0; unsigned int duplicateValue = 0; - for(unsigned int v = 1; v <= maxVal; v++) - { - if(counts[v] == 2) { duplicatesFound++; duplicateValue = v; } - else if(counts[v] > 2) { fprintf(stderr, "value %u appears more than twice\n", v); free(nums); free(counts); return 4; } + + for (unsigned int v = 1; v <= maxVal; v++) { + if (counts[v] == 2) { + duplicatesFound++; + duplicateValue = v; // den doppelten Wert merken + } else if (counts[v] > 2) { + fprintf(stderr, "value %u appears more than twice\n", v); + free(nums); + free(counts); + return 4; // Vertragsbruch: zu häufiges Auftreten + } + // counts[v] == 0 oder 1 sind unkritisch } - if(duplicatesFound != 1) { fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound); free(nums); free(counts); return 5; } + if (duplicatesFound != 1) { + fprintf(stderr, "expected exactly one duplicated value, found %d\n", duplicatesFound); + free(nums); + free(counts); + return 5; // zu wenige/zu viele Duplikate + } + // Ergebnis von getDuplicate() mit der Zählung abgleichen unsigned int found = getDuplicate(nums, len); - if(found != duplicateValue) { fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue); free(nums); free(counts); return 6; } + if (found != duplicateValue) { + fprintf(stderr, "getDuplicate returned %u expected %u\n", found, duplicateValue); + free(nums); + free(counts); + return 6; // Abweichung zwischen Methoden + } + // Ressourcen freigeben und Erfolg melden free(nums); free(counts); - printf("test_numbers: OK (duplicate = %u)\n", duplicateValue); return 0; } diff --git a/test_stack b/test_stack deleted file mode 100755 index fe4df87..0000000 Binary files a/test_stack and /dev/null differ diff --git a/test_stack.exe b/test_stack.exe new file mode 100644 index 0000000..b2c0199 Binary files /dev/null and b/test_stack.exe differ diff --git a/timer.o b/timer.o deleted file mode 100644 index c5283e8..0000000 Binary files a/timer.o and /dev/null differ