es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué la función de eliminación de mi tabla hash no elimina completamente la entrada de datos?

Intento implementar una tabla hash en C. Estoy utilizando direccionamiento abierto (sondeo lineal) para resolver colisiones, de manera que si el programa intenta crear una nueva entrada y la clave tiene un hash que ya está en uso, la entrada ocupará la siguiente posición disponible. Finalmente logré que mis funciones de búsqueda e inserción dejaran de dar segfault, pero mi función de eliminación no elimina correctamente las entradas de la tabla hash y no estoy seguro de por qué.

Mi tabla hash está implementada como un arreglo de punteros a estructuras Item. La estructura Item contiene una clave de cadena y un valor de datos (sólo un entero por ahora). Mi función de eliminación llama a free() en el puntero al elemento que necesita eliminar y establece ese valor en el arreglo como NULL. Sin embargo, la función parece eliminar únicamente la cadena de clave de la estructura Item. Llamadas subsiguientes a ht_search() con la misma clave arrojan NULL como se espera, pero un puntero al elemento antes de la eliminación todavía permite que el programa acceda al valor del elemento. No estoy seguro de por qué esto sucede y no puedo descubrirlo.

Partes relevantes de mi código: (Avíseme si debo incluir funciones adicionales como ht_insert() y ht_search() en mi ejemplo.)


#define HASHTABLE_SIZE 1000 #define KEY_SIZE 64 typedef struct { char key[KEY_SIZE]; int value; } Item; Item *hashtable[HASHTABLE_SIZE]; unsigned int ht_hash(const char *key) { unsigned int sum = 0; for (; *key; ++key) { sum = sum * 101 + *key; } sum %= HASHTABLE_SIZE; return sum; } Item *ht_delete(const char *key) { unsigned int hash = ht_hash(key); unsigned int i = hash; for (;; ++i) { i %= HASHTABLE_SIZE; if (hashtable[i] && !strcmp(key, hashtable[i]->key)) break; if (i == hash) return -1; } free(hashtable[i]); hashtable[i] = NULL; return hashtable[i]; } int main(void) { Item *sel; char *s = "foobar"; int data = 12345; /* todo está bien hasta aquí */ sel = ht_insert(s, data); /* esto establece el campo 'key' en sel como una cadena vacía, * ¿pero el campo de valor todavía es accesible? */ ht_delete(s); /* sel se vuelve nulo */ sel = ht_search(s); return 0; }

¿Alguien sabe por qué ht_delete no elimina completamente la estructura Item? ¿Qué está sucediendo?

Tags:  ,

Answer

  1. Avatar for davy.ai

    Parece que la función ht_delete está liberando correctamente la memoria en el índice i del array de la tabla hash, pero es importante tener en cuenta que sel todavía apunta a la misma dirección de memoria donde solía estar la estructura Item. Cuando se llama a ht_delete, establece el valor en hashtable[i] en NULL, por lo que las llamadas posteriores a ht_search() con la misma clave devolverán NULL. Sin embargo, como sel todavía apunta a la misma dirección de memoria, es posible acceder al campo de valor de la estructura Item a través de sel, aunque la memoria real se haya liberado.

    Para solucionar esto, debes asegurarte de que no se utilicen más punteros a la estructura Item después de que se haya eliminado. En tu código de ejemplo, simplemente puedes establecer sel en NULL después de llamar a ht_delete() para evitar cualquier acceso adicional a la memoria eliminada:

    sel = ht_search(s); // esto devolverá NULL
    ht_delete(s);
    sel = NULL; // asegúrate de invalidar cualquier puntero a estructuras Item
    

    Alternativamente, podrías crear una función separada para manejar la liberación de memoria de una estructura Item y llamar a esa función desde ht_delete() y cualquier otra función que necesite liberar memoria asociada con una estructura Item:

    void free_item(Item *item)
    {
        if (item != NULL) {
            free(item);
        }
    }
    
    Item *ht_delete(const char *key)
    {
        unsigned int hash = ht_hash(key);
        unsigned int i = hash;
    
        for (;; ++i) {
            i %= HASHTABLE_SIZE;
    
            if (hashtable[i] && !strcmp(key, hashtable[i]->key))
                break;
            if (i == hash)
                return -1;
        }
    
        Item *deleted_item = hashtable[i];
        hashtable[i] = NULL;
        free_item(deleted_item);
    
        return deleted_item;
    }
    

    Al crear una función separada para liberar una estructura Item, puedes asegurarte de que cualquier puntero a la estructura se invalide al mismo tiempo que se libera la memoria.

Comments are closed.