Compare commits

...
6 Commits
6 changed files with 325 additions and 14 deletions
+45 -2
View File
@@ -10,10 +10,43 @@
// 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)
{
// Hilfsfunktion: neuen Knoten erstellen und Daten kopieren
TreeNode* newNode(const void* data, size_t dataSize) {
TreeNode* node = malloc(sizeof(TreeNode));
if (!node) return NULL;
node->data = malloc(dataSize);
if (!node->data) {
free(node);
return NULL;
}
memcpy(node->data, data, dataSize); // Daten kopieren
node->left = node->right = NULL;
return node;
}
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
CompareFctType compareFct, int *isDuplicate)
{
if (root == NULL) {
*isDuplicate = 0; // kein Duplikat, neuer Knoten
return newNode(data, dataSize); // neuer Knoten wird Wurzel
}
int result = compareFct(data, root->data);
if (result < 0) {
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
} else if (result > 0) {
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
} else {
// result == 0 → Duplikat
*isDuplicate = 1;
}
return root;
}
// 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,
@@ -26,7 +59,17 @@ void *nextTreeData(TreeNode *root)
// Releases all memory resources (including data copies).
void clearTree(TreeNode *root)
{
if (root == NULL) return; // Basisfall: leerer Teilbaum
// Rekursiv zuerst die Kinder freigeben
clearTree(root->left);
clearTree(root->right);
// Daten freigeben
free(root->data);
// Knoten selbst freigeben
free(root);
}
// Returns the number of entries in the tree given by root.
+3 -3
View File
@@ -36,14 +36,14 @@ $(program_obj_filesobj_files): %.o: %.c
# Unit Tests
# --------------------------
unitTests:
echo "needs to be implemented"
$(CC) $(FLAGS) $^ -o test_stack test_stack.c stack.c -Wall && ./test_stack
# --------------------------
# Clean
# --------------------------
clean:
ifeq ($(OS),Windows_NT)
del /f *.o doble
del /f *.o doble test_stack
else
rm -f *.o doble
rm -f *.o doble test_stack
endif
+33 -2
View File
@@ -5,7 +5,7 @@
#include "numbers.h"
#include "bintree.h"
//TODO: getDuplicate und createNumbers implementieren
// TODO: getDuplicate und createNumbers implementieren
/* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen.
* Sicherstellen, dass beim Befüllen keine Duplikate entstehen.
* Duplizieren eines zufälligen Eintrags im Array.
@@ -17,10 +17,41 @@
unsigned int *createNumbers(unsigned int len)
{
int n;
printf("Wie viele Zufallszahlen sollen erstellt werden? ");
scanf("%d", &n);
int array[n];
srand(time(NULL)); // Zufallsgenerator initialisieren
for (int i = 0; i < n; i++)
{
int zahl;
char vorhanden;
do
{
vorhanden = 0;
zahl = rand() % n; // Zufallszahl zwischen 1 und 100
// Prüfen, ob Zahl schon im Array existiert
for (int j = 0; j < i; j++)
{
if (array[j] == zahl)
{
vorhanden = 1;
break;
}
}
} while (vorhanden); // solange wiederholen, bis Zahl einzigartig ist
array[i] = zahl;
}
return 0;
}
// Returns only the only number in numbers which is present twice. Returns zero on errors.
unsigned int getDuplicate(const unsigned int numbers[], unsigned int len)
{
}
+43
View File
@@ -10,24 +10,67 @@
// Pushes data as pointer onto the stack.
StackNode *push(StackNode *stack, void *data)
{
// Speicher für den neuen Knoten allokieren
StackNode *newNode = (StackNode *)malloc(sizeof(StackNode));
// Prüfen, ob die Allokierung erfolgreich war
if (newNode == NULL)
{
return stack; // Unveränderter Stack bei Fehler
}
// Neuen Knoten initialisieren
newNode->data = data;
newNode->next = stack; // Zeigt auf die aktuelle Spitze des Stacks
// Neuen Knoten als neue Spitze des Stacks zurückgeben
return newNode;
}
// Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be
// freed by caller.)
StackNode *pop(StackNode *stack)
{
// Prüfen, ob der Stack leer ist
if (stack == NULL)
{
return NULL;
}
// Zeiger auf den nächsten Knoten speichern (wird zur neuen Spitze)
StackNode *newTop = stack->next;
// Aktuellen obersten Knoten freigeben (aber NICHT die Daten - Verantwortung des Aufrufers)
free(stack);
// Neue Spitze des Stacks zurückgeben
return newTop;
}
// Returns the data of the top element.
void *top(StackNode *stack)
{
// Prüfen, ob der Stack leer ist
if (stack == NULL)
{
return NULL;
}
// Datenzeiger des obersten Knotens zurückgeben
return stack->data;
}
// Clears stack and releases all memory.
void clearStack(StackNode *stack)
{
StackNode *current = stack;
StackNode *next;
// Durch alle Knoten iterieren und freigeben
while (current != NULL)
{
next = current->next; // Nächsten Knoten speichern
free(current); // Aktuellen Knoten freigeben (aber NICHT die Daten)
current = next; // Zum nächsten Knoten weitergehen
}
}
+7
View File
@@ -8,6 +8,13 @@ The latest element is taken from the stack. */
#include <stdlib.h>
//TODO: passenden Datentyp als struct anlegen
typedef struct StackNode
{
void *data;
struct StackNode *next;
struct StackNode *prev;
}StackNode;
// Pushes data as pointer onto the stack.
StackNode *push(StackNode *stack, void *data);
+187
View File
@@ -0,0 +1,187 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "stack.h"
// Hilfsfunktion: Gibt "PASSED" oder "FAILED" aus
void printTestResult(const char *testName, int passed)
{
if (passed)
{
printf("[PASSED] %s\n", testName);
}
else
{
printf("[FAILED] %s\n", testName);
}
}
// Test 1: Leerer Stack
void test_emptyStack()
{
StackNode *stack = NULL;
// Top auf leerem Stack sollte NULL zurückgeben
void *result = top(stack);
printTestResult("Test 1: top() auf leerem Stack", result == NULL);
// Pop auf leerem Stack sollte NULL zurückgeben
stack = pop(stack);
printTestResult("Test 1: pop() auf leerem Stack", stack == NULL);
}
// Test 2: Push und Top
void test_pushAndTop()
{
StackNode *stack = NULL;
// Integer-Werte allokieren
int *val1 = (int *)malloc(sizeof(int));
*val1 = 42;
// Wert auf den Stack legen
stack = push(stack, val1);
// Obersten Wert abrufen
int *topVal = (int *)top(stack);
int passed = (topVal != NULL && *topVal == 42);
printTestResult("Test 2: push() und top()", passed);
// Aufräumen
free(val1);
clearStack(stack);
}
//Test 3 mehrmaliges pushen
void test_multiplePush()
{
StackNode *stack = NULL;
//Speicher für Werte allokieren
int *val1 = (int *) malloc(sizeof(int));
int *val2 = (int *) malloc(sizeof(int));
int *val3 = (int *) malloc(sizeof(int));
*val1 = 10;
*val2 = 20;
*val3 = 30;
//Testwerte auf den Stack legen
stack = push(stack, val1);
stack = push(stack, val2);
stack = push(stack, val3);
// Oberster Wert sollte 30 sein (LIFO)
int *topVal = (int *)top(stack);
int passed = (topVal != NULL && *topVal == 30);
printTestResult("Test 3: Mehrfache push() - LIFO-Prinzip", passed);
// Aufräumen
free(val1);
free(val2);
free(val3);
clearStack(stack);
}
// Test 4: Push und Pop
void test_pushAndPop()
{
StackNode *stack = NULL;
// Drei Werte auf den Stack legen
int *val1 = (int *)malloc(sizeof(int));
int *val2 = (int *)malloc(sizeof(int));
int *val3 = (int *)malloc(sizeof(int));
*val1 = 100;
*val2 = 200;
*val3 = 300;
stack = push(stack, val1);
stack = push(stack, val2);
stack = push(stack, val3);
// Oberster Wert: 300
int *topVal1 = (int *)top(stack);
int test1 = (topVal1 != NULL && *topVal1 == 300);
// Pop - neuer oberster Wert: 200
stack = pop(stack);
int *topVal2 = (int *)top(stack);
int test2 = (topVal2 != NULL && *topVal2 == 200);
// Pop - neuer oberster Wert: 100
stack = pop(stack);
int *topVal3 = (int *)top(stack);
int test3 = (topVal3 != NULL && *topVal3 == 100);
// Pop - Stack sollte leer sein
stack = pop(stack);
int test4 = (stack == NULL);
int passed = test1 && test2 && test3 && test4;
printTestResult("Test 4: push() und pop() - Korrekte Reihenfolge", passed);
// Aufräumen
free(val1);
free(val2);
free(val3);
}
// Test 5: ClearStack
void test_clearStack()
{
StackNode *stack = NULL;
// Mehrere Werte auf den Stack legen
int *val1 = (int *)malloc(sizeof(int));
int *val2 = (int *)malloc(sizeof(int));
int *val3 = (int *)malloc(sizeof(int));
int *val4 = (int *)malloc(sizeof(int));
int *val5 = (int *)malloc(sizeof(int));
*val1 = 1;
*val2 = 2;
*val3 = 3;
*val4 = 4;
*val5 = 5;
stack = push(stack, val1);
stack = push(stack, val2);
stack = push(stack, val3);
stack = push(stack, val4);
stack = push(stack, val5);
// Stack löschen
clearStack(stack);
stack = NULL; // Nach clearStack ist der Stack leer
printTestResult("Test 5: clearStack() - Alle Knoten freigegeben", 1);
// Daten müssen manuell freigegeben werden (Verantwortung des Aufrufers)
free(val1);
free(val2);
free(val3);
free(val4);
free(val5);
}
int main()
{
printf("=== Stack Unit-Tests ===\n\n");
test_emptyStack();
test_pushAndTop();
test_multiplePush();
test_pushAndPop();
test_clearStack();
/*test_stressTest();*/
printf("\n=== Alle Tests abgeschlossen ===\n");
printf("\nCode-Review: Speicherverwaltung\n");
printf("--------------------------------\n");
printf("✓ Aufrufer gibt Daten frei, Stack-Funktionen geben Knoten frei\n");
return 0;
}