C Dynamische ArrayList - Speicher freigeben

boFFeL

Cadet 4th Year
Registriert
Juli 2010
Beiträge
73
Guten Tag zusammen,

ich hab ein Problem bei meinem C Programm. Habe eine ArrayList programmiert, die dynamisch Speicher anfordert und freigibt und beliebige Elemente aufnimmt. Alle Funktionen funktionieren auch soweit wie gewollt. Nur beim Freigeben der gesamten Liste hapert es irgendwie.

Hier erstmal meine ArrayList Definition:
Code:
typedef struct
{
    void **elements;
    int element_count;
    int size;
    EQUALS equal_func;
    DESTROY destroy_func;
} ARRAY_LIST;
Soll also eine beliebige Anzahl von void Pointern aufnehmen.

Den Funktionszeiger für Freigeben-Funktion habe ich wie folgt definiert:
Code:
typedef void (*DESTROY) (void **);
Hier die Destroy Funtkion bei der es knallt, jedoch auch nur wenn destroy_data = TRUE ist. Also scheint ja irgendwas mit dem Funktionszeiger destroy_func nicht richtig zu funktionieren.
Code:
extern void array_list_destroy(BOOL destroy_data, ARRAY_LIST **list)
{
    int current_index;
    if (list == NULL || *list == NULL)
    {
        return;
    }
    current_index = 0;
    if ((*list)->destroy_func != NULL && destroy_data)
    {
        while (current_index < (*list)->size)
        {
            (*list)->destroy_func(&((*list)->elements[current_index]));
            current_index++;
        }
    }
    
    free(*list);
    *list = NULL;
}

Beim Entfernen von Elementen nutze ich den Funktionszeiger jedoch auch und da scheint es keine Probleme zu geben.
Code:
extern BOOL array_list_remove_index(int index, ARRAY_LIST *list)
{
    array_list_not_null(list);
    if (index < 0 || index > list->element_count - 1)
    {
        return FALSE;
    }
    
    if (index < list->element_count - 1)
    {
        /* Zu entfernendes Element liegt nicht am Ende, also müssen alle Folge-
         * elemente um eine Position vorgerückt werden. */
        int current_index = index;
        while (current_index < list->element_count - 1)
        {
            list->elements[current_index] = list->elements[current_index + 1];
            current_index++;
        }
    }
    
    list->element_count--;
    
    /* Speicherplatz des entfernten Elements freigeben. */
    if (list->destroy_func != NULL)
    {
        list->destroy_func(&(list->elements[list->element_count]));
    }
    else
    {
        list->elements[list->element_count] = NULL;
    }
    
    /* Prüfen ob Speicherplatz freigegeben werden kann. */
    if (list->size - list->element_count > ALLOC_ELEMENTS)
    {
        array_list_realloc(list->size - ALLOC_ELEMENTS, 
                           "array_list_remove_index", &list);
    }
    
    return TRUE;    
}

Zum Testen habe ich mir dann schnell ein Modul Point geschrieben, welches wie folgt aussieht:
Code:
extern POINT *new_point(int x, int y)
{
    POINT *point = malloc(sizeof(POINT));
    if (point != NULL)
    {
        point->x = x;
        point->y = y;
    }
    return point;
}

extern void destroy_point(POINT **point)
{
    free(*point);
    *point = NULL;
}

extern BOOL point_equals(POINT *point1, POINT *point2)
{
    return (point1->x == point2->x) && (point1->y == point2->y);
}

Und mein C Simple Test sieht folgendermaßen aus:
Code:
test_array_list_destroy()
{
    printf("test_array_list_destroy\n");
    ARRAY_LIST *list = new_array_list((EQUALS) point_equals, 
                                      (DESTROY) destroy_point);
    POINT *point1 = new_point(1, 2);
    POINT *point2 = new_point(3, 4);
    
    array_list_add_append(point1, list);
    array_list_add_append(point2, list);
    array_list_add_append(point1, list);
    array_list_destroy(TRUE, &list);
    if (point1 != NULL || point2 != NULL)
    {
        printf("%%TEST_FAILED%% time=0 testname=test_array_list_destroy "
            "(array_list_test) message=Speicher des Elements wurde nicht "
                "korrekt freigegeben.\n");
    }
}

Wenn ich den Test ausführe, kommt folgender Fehler:
/bin/sh: line 6: 4064 Aborted (core dumped) ./build/Debug/Cygwin_4.x-Windows/tests/TestFiles/f2

Außerdem springt mein Spybot S&D an, wenn ich den Test debuggen will:
spybot-jpg.273044

Mache ich irgendwo etwas falsch? Bin auf dem Gebiet Funktionszeiger noch ganz neu und dynamische Speicherverwaltung auch nocht nicht sonderlich erfahren.

Den kompletten Sourcecode gibt es als Anhang.

Bin für jegliche Hilfe dankbar.

Greez boFFeL


EDIT: Ich hab grade bemerkt, dass ich meinem Test natürlich versehentlich Point1 2 mal hinzufüge und dann versucht wird ein free auf einem Pointer mit dem Wert NULL durchzuführen (Insofern es funktioniert, wie es soll).
Entfernen dieser Codezeile führt zu keinem Fehler mehr in Netbeans. Bestanden wird der Testfall jedoch nicht und die Spybot Meldung kommt weiterhin.
 

Anhänge

Zuletzt bearbeitet:
Scheinst dein Problem ja bereits gelöst zu haben (zumindest das C-Problem ... wieso dein Spybot-Dingens da aufmuckt, weiß ich nicht :p).

Trotzdem eine Frage ...

Code:
POINT *point = malloc(sizeof(POINT));

So was läßt dir dein Compiler durchgehen? Meines Erachtens gibt malloc() einen void-Pointer zurück, den man erst explizit auf den gewünschten Typ casten muß.
 
Die Spybot Meldung erklärt sich mit einem illegalen Speicherzugriff. Dies kann unter kontrollierten Bedingungen dazu führen, dass das betroffene System kompromitiert wird. Um das zu verhindern wird der betreffende Prozess von Spybot abgewürgt.
 
antred schrieb:
Trotzdem eine Frage ...

Code:
POINT *point = malloc(sizeof(POINT));

So was läßt dir dein Compiler durchgehen? Meines Erachtens gibt malloc() einen void-Pointer zurück, den man erst explizit auf den gewünschten Typ casten muß.

Da hast du natürlich vollkommen Recht. Aber den GCC meiner Cygwin Umgebung scheint das nicht sonderlich zu stören. Werde ich dennoch korrigieren, wenn ich wieder zu hause bin. Wer weiß, was für Seiteneffekte das hat.

blackst0rm schrieb:
Die Spybot Meldung erklärt sich mit einem illegalen Speicherzugriff. Dies kann unter kontrollierten Bedingungen dazu führen, dass das betroffene System kompromitiert wird. Um das zu verhindern wird der betreffende Prozess von Spybot abgewürgt.

Ja das klingt einleuchtend. Find ich persönlich nur schwer zu lokalisieren. Werde nachher Spybot mal abschalten und schauen, ob ich beim debuggen die Stelle finde.


Danke für die Antworten schonmal.
 
antred schrieb:
Code:
POINT *point = malloc(sizeof(POINT));

So was läßt dir dein Compiler durchgehen? Meines Erachtens gibt malloc() einen void-Pointer zurück, den man erst explizit auf den gewünschten Typ casten muß.

Hat sich hier ein C++ler in einen C-Thread verlaufen? :/
Nein, man muss in C nicht casten.
 
Ach ok, implizit. Wusste jetzt nur, dass ein void* zurückgegeben wird.

Und wir haben in der Uni immer nochmal gecastet. Von wegen guter Stil etc...
Ergänzung ()

Kann es sein, dass ich auch auf jeden Fall in meinem Test was falsch mache?

Code:
array_list_add_append(point1, list);
array_list_add_append(point2, list);

Die Pointer point1 und point2 werden an sich ja nur Call-by-value übergeben.

Wenn ich also im Test überprüfe, ob
Code:
if (point1 != NULL || point2 != NULL)
können diese ja gar nicht NULL sein oder? Sondern nur die in der Liste einsortierten Kopien müssten NULL sein und der Speicher, auf den beide zeigen, dürfte freigegeben sein. Das würde erklären, warum der Test fehlschlägt.

Warum ich beim Debuggen des Tests aber scheinbar eine Speicherverletzung kriege, verstehe ich nicht. Ich dereferenziere die Pointer doch nicht mehr. Und selbst wenn ich im Test nur prüfe, ob
Code:
if (list != NULL)
bekomme ich das Spybot Problem im Debug. Wenn ich Run Test mache läuft alles durch.

Hier nochmal kurz, was ich zum Anfügen eines Elements mache.
Code:
extern void array_list_add_append(void *element, ARRAY_LIST *list)
{
    array_list_not_null(list);
    /* Ggf. neuen Speicher für die Elemente anfordern. */
    if (list->element_count == list->size)
    {        
        array_list_realloc(list->size + ALLOC_ELEMENTS, "array_list_add_append", 
                           &list);
    }
    list->elements[list->element_count] = element;
    list->element_count++;
}

static void array_list_realloc(int new_size, char *function_name, 
                               ARRAY_LIST **list)
{
        (*list)->elements = realloc((*list)->elements, new_size 
                * sizeof(void *));
        ENSURE_ENOUGH_MEMORY((*list)->elements, function_name)
        (*list)->size = new_size;
}

ENSURE_ENOUGH_MEMORY ist ein Makro, welches das das Programm einfach beendet, wenn nicht genug Speicher reserviert werden konnte, der übergebene Pointer also NULL ist.

Werde leider nicht ganz schlau raus :(
 
Zurück
Oben