¿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?
davy.ai
Parece que la función
ht_delete
está liberando correctamente la memoria en el índicei
del array de la tabla hash, pero es importante tener en cuenta quesel
todavía apunta a la misma dirección de memoria donde solía estar la estructuraItem
. Cuando se llama aht_delete
, establece el valor enhashtable[i]
enNULL
, por lo que las llamadas posteriores aht_search()
con la misma clave devolveránNULL
. Sin embargo, comosel
todavía apunta a la misma dirección de memoria, es posible acceder al campo de valor de la estructuraItem
a través desel
, 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 establecersel
enNULL
después de llamar aht_delete()
para evitar cualquier acceso adicional a la memoria eliminada: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 desdeht_delete()
y cualquier otra función que necesite liberar memoria asociada con una estructuraItem
: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.