I'm trying to delete a value from hash table, the code works when I delete a value from the array where the linked list size is one, then segfaults if the size is greater than 1.
typedef struct hash {
char *key;
char *value;
struct hash *next;
} hashTable;
// generate hash code
unsigned int hashCode(char *key) {
int sum;
int i;
sum = 0;
i = 0;
while (key[i])
sum += key[i++];
return sum % MAX_HASH;
}
// get hash item size
int hash_size(hashTable *head) {
int i;
hashTable *list;
i = 0;
list = head;
if (list) {
while (list != NULL) {
i++;
list = list->next;
}
}
return (i);
}
// free item
void free_hash(hashTable *item) {
free(item->key);
free(item->value);
free(item);
}
// function for deleting item from hash table
void deleteItem(hashTable *table[], char *key) {
hashTable *head = table[hashCode(key)];
hashTable *tmp = head;
hashTable *prev = NULL;
if (!head)
return;
if (hash_size(tmp) == 1) {
table[hashCode(key)] = 0;
free_hash(tmp);
return;
}
while (strcmp(tmp->key, key) != 0 && tmp->next != NULL) {
prev = tmp;
tmp = tmp->next;
}
if (strcmp(tmp->key, key) == 0) {
if (prev)
prev->next = tmp->next;
else
head = tmp->next;
free_hash(tmp);
}
}
// function for inserting item into the table
void insert(hashTable *table[], char *key, char *value) {
hashTable *tmp;
hashTable *item;
unsigned int code;
item = (hashTable *)malloc(sizeof(hashTable));
if (!item)
return;
item->key = (char *)malloc(sizeof(char) * strlen(key) + 1);
item->value = (char *)malloc(sizeof(char) * strlen(value) + 1);
item->next = NULL;
code = hashCode(key);
strcpy(item->key, key);
strcpy(item->value, value);
if (!table[code])
table[code] = item;
else {
tmp = table[code];
item->next = tmp;
table[code] = item;
}
}
// displaying items
void display(hashTable *table[]) {
int i = 0;
hashTable *tmp;
while (i < MAX_HASH) {
if (table[i] != NULL) {
tmp = table[i];
if (hash_size(tmp) == 1)
printf("%s=%s\n", tmp->key, tmp->value);
else {
while (tmp != NULL) {
printf("%s=%s\n", tmp->key, tmp->value);
tmp = tmp->next;
}
}
}
i++;
}
}
int main(int argc, char const *argv[]) {
hashTable *table[MAX_HASH];
memset(table, 0, MAX_HASH * sizeof(hashTable *));
insert(table, "Bart", "first");
insert(table, "Lisa", "Second");
insert(table, "Foo", "bar");
deleteItem(table, "Lisa");
display(table);
return 0;
}
There are many issues in your code:
do include the standard header files, and define HASH_MAX:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASH_MAX 1027
the type hashTable is confusing: it is really an entry list, the hash table itself is the array.
the while loops are error prone: use the much preferred for loops where the initialization, test and increment of the loop index are conveniently located on the same line:
for (int i = 0; i < HASH_MAX; i++) {
// printf hashTable[i]
}
I know the local style conventions at 42 explicitly exclude the for loop, but you should lobby against this questionable choice.
there is no need to special case hash_size(tmp) == 1 in display_table()
there is no need to cast the return value of malloc(). sizeof(char) is 1 by definition. You could use strdup() to duplicate C strings.
in deleteItem() you always remove the entry if it is alone: this is incorrect if the entry has a different key. Furthermore, you do not link the previous node or the table slot to the next element of the list.
Here is a corrected version of this function:
// function for deleting item from hash table
void deleteItem(hashTable *table[], const char *key) {
hashTable **link = &table[hashCode(key)];
while (*link) {
hashTable *tmp = *link;
if (strcmp(tmp->key, key) == 0) {
*link = tmp->next; // unlink the list node
free_hash(tmp);
break; // remove this if you mean for deleteItem to remove all matching nodes
} else {
link = &(*link)->next;
}
}
}
Here is a simplified version of the whole program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_HASH 1027
typedef struct HashItem {
char *key;
char *value;
struct HashItem *next;
} HashItem;
// generate hash code
unsigned int hashCode(const char *key) {
unsigned int sum = 0;
for (int i = 0; key[i]; i++) {
sum += (unsigned char)key[i] * (i + 1);
}
return sum % MAX_HASH;
}
// free item
void freeItem(HashItem *item) {
free(item->key);
free(item->value);
free(item);
}
// function for deleting item from hash table
void deleteItem(HashItem *table[], const char *key) {
HashItem **link = &table[hashCode(key)];
while (*link) {
HashItem *tmp = *link;
if (strcmp(tmp->key, key) == 0) {
*link = tmp->next; // unlink the list node
freeItem(tmp);
break;
} else {
link = &(*link)->next;
}
}
}
// function for inserting item into the table
void insertItem(HashItem *table[], const char *key, const char *value) {
unsigned int code = hashCode(key);
HashItem *item = malloc(sizeof(*item));
if (item != NULL) {
item->key = strdup(key);
item->value = strdup(value);
item->next = table[code];
table[code] = item;
}
}
// displaying items
void displayHashTable(HashItem *table[]) {
for (int i = 0; i < MAX_HASH; i++) {
for (HashItem *tmp = table[i]; tmp; tmp = tmp->next) {
printf("%s=%s\n", tmp->key, tmp->value);
}
}
}
int main(int argc, char const *argv[]) {
HashItem *table[MAX_HASH] = { 0 };
insertItem(table, "Bart", "first");
insertItem(table, "Lisa", "Second");
insertItem(table, "Foo", "bar");
deleteItem(table, "Lisa");
displayHashTable(table);
return 0;
}
Related
Problem is function bin_search. it works steady on function insert. However, it gets frozen on function search. I think if it's fine on insert, it should be fine on search, but it isn't. Here is my code...
"bst.h":
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
int key;
void *data;
struct Node *left, *right;
void (*destroy)(void *data);
} node;
typedef struct Tree {
node *head;
char name;
} tree;
#define key(node) node->key
#define data(node) node->data
#define left(node) node->left
#define right(node) node->right
#define destroy(node) node->destroy
#define tree_head(tree) tree->head
"functions.c":
#include "bst.h"
int bin_search(node *curr, int key, int cnt, node **found) {
cnt++; printf("cnt+\n");
if (curr == NULL) {
return -1;
} else if (curr->key == key) {
printf("curr_key = key\n"); return cnt;
}
if (curr->key < key) {
printf("curr_key < key\n");
if (curr->right == NULL) {
*found = curr;
return -(cnt + 1);
}
return bin_search(curr->right, key, cnt, found);
} else {
printf("curr_key > key\n");
if (curr->left == NULL) {
*found = curr;
return -(cnt + 1);
}
return bin_search(curr->left, key, cnt, found);
}
}
int insert(tree *root, int key, void *data, void (*destroy)(void *data)) {
if (root->head == NULL) {
node* new_node = (node *)malloc(sizeof(node));
left(new_node) = NULL; right(new_node) = NULL; destroy(new_node) = destroy; key(new_node) = key; data(new_node) = data;
tree_head(root) = new_node;
printf("created first node\n"); return 1;
}
int cnt; node **found;
if ((cnt = bin_search(root->head, key, 0, found)) < 0) {
node* new_node = (node *)malloc(sizeof(node));
left(new_node) = NULL; right(new_node) = NULL; destroy(new_node) = destroy; key(new_node) = key; data(new_node) = data;
if ((*found)->key < key) {
(*found)->right = new_node;
} else {
(*found)->left = new_node;
}
printf("created a node at %d\n", -cnt); return 1;
} else {
printf("already exists in tree"); return -1;
}
}
int search(tree *root, int key, void **data) {
if (root->head == NULL) {
printf("tree is empty\n"); return -1;
}
int cnt; node **found;
if ((cnt = bin_search(root->head, key, 0, found)) < 0) {
return -1;
} else {
if ((*found)->key < key) {
*data = (*found)->right->data;
} else {
*data = (*found)->left->data;
}
return cnt;
}
}
"main.c":
#include "bst.h"
#define MAX_NUM 8
#define MAX_LEGNTH 200
int main() {
// create a tree
tree root; root.head = NULL; root.name = 'a';
FILE *inpt = fopen("list.txt", "r"); char buffer[MAX_LEGNTH];
int count = 0;
while (fgets(buffer, MAX_LEGNTH, inpt) != NULL) {
printf("adding: %d\n", atoi(buffer)); insert(&root, atoi(buffer), buffer, NULL);
count++;
}
fclose(inpt);
int result; void **found;
result = search(&root, 2, found); printf("%d\n", result); // problem in here!!
return 0;
}
what is the problem with my 'search' function. I can't find it.
Besides the utterly non-standard use of sizeof(void), you are not providing a correct out-parameter to search. The point of the found argument is to receive the node pointer if the prospect key was discovered. This...
int cnt;
node **found; // <== LOOK HERE
if ((cnt = bin_search(root->head, key, 0, found)) < 0) {
is not the way to do that. It should be done like this:
int cnt;
node *found = NULL;
if ((cnt = bin_search(root->head, key, 0, &found)) < 0) {
// ^^^^^^
and all references to found thereafter should be through found->, not (*found)->
This mistake is made in three different places in your code. The last one is semi-broken, but still broken nonetheless.
void **found = (void *)malloc(sizeof(void));
int result = search(&root, 2, found);
printf("%d\n", result);
That should use this:
void *found = NULL;
int result = search(&root, 2, &found);
printf("%d\n", result);
Whether the rest of your code is broken I cannot say, and frankly we're not in the business of being an online-debugger. Use your local debugger tools; that's what they're for. But the items listed above are definitely a problem.
I am coding a hash table with singly linkedList and I have this problem free(): double free detected in tcache 2 I tried to fix it but did'nt make it, the problem its the free() , so could you explain me why I have it, So if anyone can help, so please help me, I'm trying to fix it for hours now...
Thank you.
I've watched some video on youtube and many topics on websitte and also here but I didn't find a solution for mine.
This are my functions:
/**List header */
#ifndef LISTE_H
#define LISTE_H
struct _list_node {
void * data;
struct _list_node *next;
};
typedef struct _list_node s_node;
s_node * list_create(void);
void * list_get_data(s_node * node);
void list_set_data(s_node * node, void * data);
s_node * list_insert(s_node * head, void * data);
s_node * list_append(s_node * head, void * data);
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
void * param, s_node ** last);
s_node * list_ordered_append(s_node ** head, int (*fct)(s_node * node, void * param),
void * param);
s_node * list_remove (s_node * head, void * data);
s_node * list_headRemove(s_node * head);
void * list_destroy(s_node * head);
void afficher_s_node(s_node * list);
int list_is_empty( s_node * node );
unsigned int list_size(s_node * node);
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
void * param, s_node ** last);
#endif
/**** c file list */
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
s_node * list_create(void)
{
return NULL;
}
void * list_get_data(s_node * node)
{
return node->data;
}
void list_set_data(s_node * node, void * data)
{
node->data = data;
}
s_node * list_insert(s_node * head, void * data)
{
s_node * node = (s_node *) malloc(sizeof(s_node));
list_set_data(node, data);
node->next = head;
return node;
}
s_node * list_append(s_node * head, void * data)
{
if (!head) return list_insert(head, data);
s_node * node = head;
while (node->next) {
node = node->next;
}
node->next = (s_node *) malloc(sizeof(s_node));
node->next->next = list_create();
list_set_data(node->next, data);
return head;
}
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
void * param, s_node ** last)
{
if (!head) return 0;
s_node * node;
for (node = head; node; node = node->next) {
if (fct(node, param) == 1) {
*last = node;
return 1;
}
}
return 0;
}
s_node * list_ordered_append(s_node ** head, int (*fct)(s_node * node, void * param),
void * param)
{
// quand elle est vide
if (!(*head)) {
*head = list_insert(*head, param);
return *head;
}
// insertion en tete
s_node * node;
if (fct(*head, param) == 1) {
*head = list_insert(*head, param);
return (*head);
}
// cas general
int res;
for(node = *head; node->next; node = node->next) {
if ((res = fct(node->next, param)) == 1) {
node->next = list_insert(node->next, param);
return node->next;
} else if (res == 0) {
return node->next;
}
}
if (fct(node, param) == 0) return node;
*head = list_append(*head, param);
return node->next;
}
s_node * list_remove (s_node * head, void * data)
{
if (!head) return head;
for (s_node * node = head; node->next; node = node->next) {
if (node->next->data == data) {
s_node * n = node->next->next;
free(node->next);
node->next = n;
break;
}
}
return head;
}
s_node * list_headRemove(s_node * head)
{
if (!head) return head;
s_node * n = head->next;
free(head);
return n;
}
void * list_destroy(s_node * head)
{
while (head)
head = list_headRemove(head);
}
void afficher_s_node(s_node * list)
{
printf("\nliste = [");
while (list) {
printf("%d,", *((int *)(list->data)));
list = list->next;
}
printf("]\n");
return;
}
int list_is_empty( s_node * node ) {
return NULL == node;
}
unsigned int list_size(s_node * node)
{
unsigned int i = 0;
while (node) {
node = node->next;
i++;
}
return i;
}
/*PLUS
int list_process(s_node * head, int (*fct)(s_node * node, void * param),
void * param, s_node ** last)
{
if (!head) return 0;
s_node * node;
for (node = head; node; node = node->next) {
if (fct(node, param) == 1) {
*last = node;
return 1;
}
}
return 0;
}
*/
/* hash table header */
#ifndef HACHAGE_H
#define HACHAGE_H
#include "list.h"
typedef struct {
s_node * node;
unsigned int len;
} super_list;
typedef struct {
super_list * list;
unsigned int len;
} strhash_table;
strhash_table * strhash_table_init(const unsigned int len);
strhash_table * strhash_table_destroy(strhash_table * table);
strhash_table * strhash_table_free(strhash_table * table);
char * strhash_table_add(strhash_table * table, char * str);
strhash_table * strhash_table_remove(strhash_table * table, char * str);
void strhash_table_info(strhash_table * table);
void strhash_print(strhash_table * table);
#endif
/**** c file hash table */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "hachage.h"
#include "list.h"
int hashCode(char * str, const int size_hash_table)
{
int i, cle = 0;
for (i = 0; str[i] != '\0'; i++) {
cle *= 2;
cle += (int) str[i];
}
return cle % size_hash_table;
}
int compare_str_add(s_node *node, void *param)
{
int res = strcmp((char *) node->data, (char *) param);
/*if the first non-matching character in node->data
is lower (in ASCII) than that of param.*/
if (res < 0) return -1;
/*if the first non-matching character in node->data
is greater (in ASCII) than that of param.*/
if (res > 0) return 1;
return 0;//if strings are equal
}
strhash_table * strhash_table_init(const unsigned int len)
{
super_list *list = (super_list *) malloc(sizeof(super_list) * len);
strhash_table * table = (strhash_table *) malloc(sizeof(strhash_table));
if(!table) return NULL;
table->len = len;
if (!list) return NULL;
for (unsigned int i = 0; i < len; i++) {
list[i].node = list_create();
list[i].len = 0;
}
table->list = list;
return table;
}
strhash_table * strhash_table_destroy(strhash_table * table)
{
unsigned int i;
super_list *list;
s_node *node, *next;
for (i = 0; i < table->len; i++) {
list = table->list + i;
node = list->node;
while (node) {
next = node->next;
free(node->data);
free(node);
node = next;
}
}
free(table->list);
free(table);
return table;
}
strhash_table * strhash_table_free(strhash_table * table)
{
unsigned int i;
super_list *list;
for (i = 0; i < table->len; i++) {
list = table->list + i;
if (list->len > 0) {
free(list->node->data);
list_destroy(list->node);
list->len = 0;
return table;
}
}
return table;
}
char * strhash_table_add(strhash_table * table, char * str)
{
char * to_insert = strdup(str);
int index = hashCode(str, table->len);
s_node *ordered_Add = list_ordered_append(&(table->list[index].node), compare_str_add, to_insert);
if (ordered_Add->data == to_insert)
table->list[index].len++;
else
free(to_insert);
return (char *) ordered_Add->data;
}
int find_str_node(s_node *node, void *param)
{
return strcmp((char *) node->data, (char *) param) == 0 ? 1 : 0;
}
strhash_table * strhash_table_remove(strhash_table * table, char * str)
{
const int index = hashCode(str, table->len);
if (table->list[index].len == 0) return table;
s_node *find_node;
const int result = list_process(table->list[index].node, &find_str_node, str, &find_node);
if (result == 1) {
free(find_node->data);
table->list[index].node = list_remove(table->list[index].node, find_node->data);
table->list[index].len--;
}
return table;
}
void strhash_table_info(strhash_table * table)
{
unsigned int i;
unsigned int len, min, max;
float deviation, moy;
len = max = min = table->list[0].len;
for (i = 1; i < table->len; i++) {
if (table->list[i].len > max) max = table->list[i].len;
else if (table->list[i].len < min) min = table->list[i].len;
len += table->list[i].len;
}
moy = (float)len / table->len;
deviation = 0;
for (i = 0; i < table->len; i++) {
deviation += (table->list[i].len - moy) * (table->list[i].len - moy);
}
deviation = (float) sqrt(deviation / table->len);
printf("Table hachage : ");
printf("%p\n",table);
printf("\tNombre total d'élément : " );
printf("%u\n",len );
printf("\tNombre minimum : ");
printf("%u\n", min);
printf("\tNombre maximum : ");
printf("%u\n", max );
printf("\tÉcart type du nombre d'éléments par entrée : ");
printf("%.2f\n", deviation );
return;
}
void strhash_print(strhash_table * table)
{
unsigned int i, j;
super_list *list;
s_node *node;
printf("\nHash table de %p\n", table);
printf("Start\n");
for (i = 0; i < table->len; i++) {
list = table->list + i;
printf("\t%d ---\n", i);
/****/
for (j = 0, node = list->node; j < list->len; j++, node = node->next)
{
printf( "\t\t%d. %s\n", j, (char *) node->data);
}
}
printf("End\n");
return;
}
/**** test file */
strhash_table * test_init(const unsigned int len)
{
strhash_table * table = strhash_table_init(len);
if (!table) {
printf("Tha HashTable hasn't been created\n");
assert(0);
}
printf("***Tha HashTable has been created***\n");
return table;
}
strhash_table * test_destroy(strhash_table * table){
table = strhash_table_destroy(table);
if (table->list->node) {
printf("The HashTable hasn't been destroyed (%p)\n", table->list->node);
assert(0);
}
printf("The HashTable has been destroyed\n");
return NULL;
}
int main(void)
{
strhash_table * table =strhash_table_init(10);
strhash_print(table);
strhash_table_add(table, "ele1");
strhash_table_add(table, "ele2");
strhash_table_add(table, "ele3");
strhash_table_add(table, "ele4");
strhash_table_add(table, "ele5");
//strhash_table_remove(table,"ele1");
//strhash_table_free(table);
test_destroy(table);
return 0;
}
Thank you in advance ^^
You should move list_destroy(list->node); outside the inner loop. You are freeing the node list multiple times inside a loop where you iterate on the node links.
Here is a modified version:
strhash_table *strhash_table_destroy(strhash_table *table) {
unsigned int i;
super_list *list;
s_node *node;
for (i = 0; i < table->len; i++) {
list = table->list + i;
for (node = list->node; node != NULL; node = node->next) {
free(node->data);
}
list_destroy(list->node);
}
free(table->list);
free(table);
return table;
}
void list_destroy(s_node *head) {
while (head) {
head = list_headRemove(head);
}
}
s_node *list_headRemove(s_node *head) {
if (!head) return head;
s_node *n = head->next;
free(head);
return n;
}
UPDATE
In the code posted, there are conflicting versions of functions list_destroy and list_headRemove, furthermore there are 2 calls to free(node); in the second function list_destroy, both of which are useless since node is a null pointer when the while loop exits.
UPDATE 2
There is a problem in strhash_table_free: you free list->node but you do not update list->node, so the list is freed a second time in strhash_table_destroy where list->len is not tested.
The field len in super_list seems redundant. You should just test if the node member is NULL and set it to NULL when the list is freed.
UPDATE 3
The final post is hardly minimal and does not show the problem... but I found some issues:
strhash_table_destroy returns table after freeing it: this is bad because table is now an invalid pointer. strhash_table_destroy should not return anything.
test_destroy dereferences table after strhash_table_destroy has freed it. This has undefined behavior. Remove this test function, just call strhash_table_destroy from main().
you free node->data in strhash_table_remove before passing it to list_remove, which is bad because node->data has become invalid.
worse: list_remove() does not test is the head node should be removed. In a minimal test case with a single element in the hash table, "ele1" is the head node, hence this node remains in the list, the len is decremented and becomes out of sync and the node has an invalid data pointer which will strhash_table_destroy will attempt to free, causing the double free issue.
strhash_table_free seems incorrect and inconsistent with strhash_table_destroy.
There are probably other issues in the code.
Here is a modified version with some simplifications and fixes:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
/**List header */
#ifndef LISTE_H
#define LISTE_H
struct _list_node {
void *data;
struct _list_node *next;
};
typedef struct _list_node s_node;
s_node *list_create(void);
void *list_get_data(s_node *node);
void list_set_data(s_node *node, void *data);
s_node *list_insert(s_node *head, void *data);
s_node *list_append(s_node *head, void *data);
s_node *list_process(s_node *head, int (*fct)(s_node *node, void *param), void *param);
s_node *list_ordered_append(s_node **head, int (*fct)(s_node *node, void *param), void *param);
s_node *list_headRemove(s_node *head);
s_node *list_remove(s_node *head, void *data);
void list_destroy(s_node *head);
void afficher_s_node(s_node *list);
int list_is_empty( s_node *node );
unsigned int list_size(s_node *node);
#endif
/**** c file list */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
//#include "list.h"
s_node *list_create(void) {
return NULL;
}
void *list_get_data(s_node *node) {
return node->data;
}
void list_set_data(s_node *node, void *data) {
node->data = data;
}
s_node *list_insert(s_node *head, void *data) {
s_node *node = (s_node *)malloc(sizeof(s_node));
if (node) {
list_set_data(node, data);
node->next = head;
return node;
} else {
return head;
}
}
s_node *list_append(s_node *head, void *data) {
if (!head) return list_insert(head, data);
s_node *node = head;
while (node->next) {
node = node->next;
}
node->next = list_insert(NULL, data);
return head;
}
s_node *list_process(s_node *head, int (*fct)(s_node *node, void *param), void *param) {
for (s_node *node = head; node; node = node->next) {
if (fct(node, param) == 1) {
return node;
}
}
return NULL;
}
s_node *list_ordered_append(s_node **head, int (*fct)(s_node *node, void *param), void *param) {
s_node *node;
int res;
// empty list
if (!*head) {
return *head = list_insert(*head, param);
}
// insert at head
res = fct(*head, param);
if (res > 0) {
return *head = list_insert(*head, param);
}
if (res == 0) {
return *head;
}
// generic case
for (node = *head; node->next; node = node->next) {
res = fct(node->next, param);
if (res > 1) {
return node->next = list_insert(node->next, param);
}
if (res == 0) {
return node->next;
}
}
// append node
return node->next = list_insert(NULL, param);
}
s_node *list_headRemove(s_node *head) {
if (head) {
s_node *n = head->next;
free(head);
return n;
} else {
return NULL;
}
}
s_node *list_remove(s_node *head, void *data) {
if (!head) return head;
if (head->data == data) {
head = list_headRemove(head);
} else {
for (s_node *node = head; node->next; node = node->next) {
if (node->next->data == data) {
node->next = list_headRemove(node->next);
break;
}
}
}
return head;
}
void list_destroy(s_node *head) {
while (head)
head = list_headRemove(head);
}
int list_is_empty(s_node *node) {
return node == NULL;
}
unsigned int list_size(s_node *node) {
unsigned int i = 0;
while (node) {
node = node->next;
i++;
}
return i;
}
/* hash table header */
#ifndef HACHAGE_H
#define HACHAGE_H
//#include "list.h"
typedef struct {
s_node *node;
unsigned int len;
} super_list;
typedef struct {
super_list *list;
unsigned int len;
} strhash_table;
strhash_table *strhash_table_init(const unsigned int len);
void strhash_table_destroy(strhash_table *table);
strhash_table *strhash_table_free(strhash_table *table);
char *strhash_table_add(strhash_table *table, const char *str);
int strhash_table_remove(strhash_table *table, const char *str);
void strhash_table_info(strhash_table *table);
void strhash_print(strhash_table *table);
#endif
/**** c file hash table */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//#include "hachage.h"
//#include "list.h"
int hashCode(const char *str, unsigned int size_hash_table) {
unsigned int i, cle = 0;
for (i = 0; str[i] != '\0'; i++) {
cle *= 2;
cle += (int)str[i];
}
return cle % size_hash_table;
}
int compare_str_add(s_node *node, void *param) {
/* return <0 if node->data is before param, >0 if after, =0 if strings are equal */
return strcmp((const char *)node->data, (const char *)param);
}
strhash_table *strhash_table_init(const unsigned int len) {
strhash_table *table = (strhash_table *)malloc(sizeof(strhash_table));
super_list *list = (super_list *)malloc(sizeof(super_list) * len);
if (!table || !list) {
free(table);
free(list);
return NULL;
}
for (unsigned int i = 0; i < len; i++) {
list[i].node = list_create();
list[i].len = 0;
}
table->list = list;
table->len = len;
return table;
}
void strhash_table_destroy(strhash_table *table) {
for (unsigned int i = 0; i < table->len; i++) {
super_list *list = table->list + i;
s_node *node = list->node;
while (node) {
s_node *next = node->next;
free(node->data);
free(node);
node = next;
}
list->node = NULL;
}
free(table->list);
free(table);
}
char *strhash_table_add(strhash_table *table, const char *str) {
char *to_insert = strdup(str);
int index = hashCode(str, table->len);
s_node *ordered_Add = list_ordered_append(&table->list[index].node, compare_str_add, to_insert);
if (ordered_Add->data == to_insert) {
/* node was inserted: increase len */
table->list[index].len++;
} else {
/* node already present: free new data */
free(to_insert);
}
return (char *)ordered_Add->data;
}
int find_str_node(s_node *node, void *param) {
return strcmp((const char *)node->data, (const char *)param) == 0 ? 1 : 0;
}
// return 1 if successful
int strhash_table_remove(strhash_table *table, const char *str) {
int index = hashCode(str, table->len);
s_node *find_node = list_process(table->list[index].node, find_str_node, (void *)(uintptr_t)str);
if (find_node) {
/* node was found: free node and data */
void *data = find_node->data;
table->list[index].node = list_remove(table->list[index].node, data);
table->list[index].len--;
free(data);
return 1;
}
return 0;
}
void strhash_table_info(strhash_table *table) {
unsigned int i;
unsigned int len, min, max;
double deviation, moy;
len = max = min = table->list[0].len;
for (i = 1; i < table->len; i++) {
if (table->list[i].len > max) max = table->list[i].len;
else if (table->list[i].len < min) min = table->list[i].len;
len += table->list[i].len;
}
moy = (double)len / table->len;
deviation = 0;
for (i = 0; i < table->len; i++) {
deviation += (table->list[i].len - moy) * (table->list[i].len - moy);
}
deviation = sqrt(deviation / table->len);
printf("Table hachage : ");
printf("%p\n", (void *)table);
printf("\tNombre total d'élément : " );
printf("%u\n",len );
printf("\tNombre minimum : ");
printf("%u\n", min);
printf("\tNombre maximum : ");
printf("%u\n", max );
printf("\tÉcart type du nombre d'éléments par entrée : ");
printf("%.2f\n", deviation);
return;
}
void strhash_print(strhash_table *table) {
unsigned int i, j;
super_list *list;
s_node *node;
printf("\nHash table de %p\n", (void *)table);
printf("Start\n");
for (i = 0; i < table->len; i++) {
list = table->list + i;
printf("\t%d ---\n", i);
/****/
for (j = 0, node = list->node; j < list->len; j++, node = node->next) {
printf( "\t\t%d. %s\n", j, (char *)node->data);
}
}
printf("End\n");
}
/**** test file */
int main(void) {
strhash_table *table = strhash_table_init(10);
strhash_print(table);
strhash_table_add(table, "ele1");
strhash_table_remove(table, "ele1");
strhash_table_add(table, "ele1");
strhash_table_add(table, "ele2");
strhash_table_add(table, "ele3");
strhash_table_add(table, "ele4");
strhash_table_add(table, "ele5");
strhash_table_remove(table, "ele1");
strhash_print(table);
strhash_table_destroy(table);
return 0;
}
A cleaner way to do it in my opinion assuming you don't reuse list_headRemove and list_headRemove :
strhash_table *strhash_table_destroy(strhash_table *table) {
unsigned int i;
super_list *list;
s_node *node, *next;
for (i = 0; i < table->len; i++) {
list = table->list + i;
node = list->node;
while (node) {
next = node->next;
free(node->data);
free(node);
node = next;
}
}
free(table->list);
free(table);
return table; /* This pointer is not valid anymore be careful */
}
I am learning C and wrote a few functions to work with linked lists in C. However, I have a memory leak (I guess because of the reassignment of the node* list in lines 28 and 29) and was looking for a way to solve it.
My code:
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int number;
struct node *next;
}
node;
node *declare(int value);
int count(node *list);
node *lead(node *list, int value);
void trail(node *list, int value);
node *append(node *list, int value, int index);
node *fetch_node(node *list, int index);
int fetch(node *list, int index);
int *list_to_array(node *list);
node *array_to_list(int array[], int range);
void free_list(node *list);
node *delete(node *list, int index);
void print(node *list);
int main(void)
{
node *list = declare(1);
list = lead(list, -1);
trail(list, 2);
trail(list, 44);
list = delete(list, 0);
append(list, 232, 1);
int *array = list_to_array(list);
node *list_new = array_to_list(array, 4);
printf("%d\n", fetch(list_new, 3));
print(list);
free_list(list);
free_list(list_new);
free(array);
}
node *declare(int value)
{
node *n = malloc(sizeof(node));
n->number = value;
n->next = NULL;
return n;
}
int count(node *list)
{
node *tmp = list;
int count = 1;
while (tmp->next != NULL)
{
tmp = tmp->next;
count++;
}
return count;
}
node *lead(node *list, int value)
{
node *n = declare(value);
node *tmp = list;
n->next = tmp;
return n;
}
void trail(node *list, int value)
{
node *n = declare(value);
node *tmp = list;
while (tmp->next != NULL)
{
tmp = tmp->next;
}
tmp->next = n;
}
node *append(node *list, int value, int index)
{
if (index == 0)
{
return lead(list, value);
}
else
{
int range = count(list);
node *n = declare(value);
node *prev = fetch_node(list, index - 1);
node *follow = fetch_node(list, index);
prev->next = n;
n->next = follow;
return list;
}
}
node *fetch_node(node *list, int index)
{
node *tmp = list;
if (index < count(list))
{
for (int i = 0; i < index; i++)
{
tmp = tmp->next;
}
}
else
{
fprintf(stderr, "VALUE IN FETCH OUT OF RANGE, RETURNING POINTER TO LIST\n");
}
return tmp;
}
int fetch(node *list, int index)
{
node *fetched = fetch_node(list, index);
return fetched->number;
}
node *delete(node *list, int index)
{
if (index == 0)
{
node *follow = list->next;
list = list->next;
}
else
{
node *fetched = fetch_node(list, index);
node *prev, *follow;
if (index > 0)
{
prev = fetch_node(list, index - 1);
}
else
{
prev = list;
}
int range = count(list);
if (index != range - 1 && index < range)
{
follow = fetch_node(list, index + 1);
}
else
{
follow = NULL;
}
prev->next = follow;
}
return list;
}
int *list_to_array(node *list)
{
int range = count(list);
int *array = malloc(sizeof(int) * range);
for (int i = 0; i < range; i++)
{
array[i] = fetch(list, i);
}
return array;
}
node *array_to_list(int array[], int range)
{
node *list = declare(array[0]);
for (int i = 1; i < range; i++)
{
trail(list, array[i]);
}
return list;
}
void free_list(node *list)
{
int range = count(list);
node *tmp = list;
node *del;
for (int i = 0; i < range; i++)
{
del = tmp;
tmp = tmp->next;
free(del);
}
}
void print(node *list)
{
int range = count(list);
printf("[");
for (int i = 0; i < range; i++)
{
int value = fetch(list, i);
printf("%d", value);
if (i != range - 1)
{
printf(", ");
}
}
printf("]\n");
}
And this is Valgrind output:
==18184== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==18184== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18184== by 0x4007C4: declare (linked_lists.c:45)
==18184== by 0x400806: lead (linked_lists.c:65)
==18184== by 0x4006F3: main (linked_lists.c:29)
Any ideas?
Your delete function is creating memory leak because it doesn't free the deleted node.
Add code to free that.
node *delete(node *list, int index)
{
if (index == 0)
{
node *follow = list->next;
// list = list->next;
free(list); // free deleted node
list = follow;
}
else
{
node *fetched = fetch_node(list, index);
node *prev, *follow;
if (index > 0)
{
prev = fetch_node(list, index - 1);
}
else
{
prev = list;
}
int range = count(list);
if (index != range - 1 && index < range)
{
follow = fetch_node(list, index + 1);
}
else
{
follow = NULL;
}
free(prev->next); // free deleted node
prev->next = follow;
}
return list;
}
I have to make linked lists that contain all the non zero values from an array and print it out. However, my code is printing out the same value for how many non zero elements there are in the array.
The creation of the linked list and printing of the linked list are two separate functions.
The addLink function creates the linked list
void addlink(DataPtr *start, int element, double value) {
Data last = *start;
Data newPtr;
newPtr = malloc(sizeof(Data));
newPtr->element = element;
newPtr->usage = value;
newPtr->next = NULL;
if(*start == NULL) {
*start = newPtr;
return;
newPtr->element = element;
newPtr->usage = value;
newPtr->next = NULL;
}
while(last->next != NULL) {
last = last->nextPtr;
last->next = newPtr;
return;
}
}
The print function prints the linked list
void print(Data *start) {
Data current = *start;
while(current != NULL) {
printf("%d ", current->element);
printf("%.3lf", current->value);
current = current->next;
}
printf("\n");
}
This is how i call it in my main
for(k = 0; k < 50; k++) {
if(values[k] != 0) {
value = values[k];
addlink(&start,k,value);
print(&start);
}
}
struct data{
int element;
double value;
struct data *next;
};
typedef struct data Data;
typedef Data *DataPtr;
DataPtr start = NULL;
Here is correct implementation of your list:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
typedef struct data {
int element;
double value;
struct data *next;
} data_t;
void print(data_t *start);
void addlink(data_t **start, int element, double value);
int main()
{
int values[50] = { 0 };
int value = 0;
int k;
data_t *start = NULL;
//Give values some value
values[0] = 100;
values[1] = 250;
for(k = 0; k < 50; k++)
{
if(values[k] != 0)
{
value = values[k];
addlink(&start,k,value);
print(start);
}
}
}
void addlink(data_t **start, int element, double value)
{
data_t *newPtr;
newPtr = malloc(sizeof(data_t));
newPtr->element = element;
newPtr->value = value;
*start = newPtr;
}
void print(data_t *start)
{
data_t *current = start;
while(current != NULL)
{
printf("%d ", current->element);
printf("%f", current->value);
current = current->next;
}
printf("\n");
}
Your DataPtr idea, is no good. The "data_t" implementation is generally what you will see when seeing linked lists. It is easiest implemented this way.
If you are trying to implement a list that adds the newest data_t to the end of the list...
Check out http://www.learn-c.org/en/Linked_lists for further explanation.
A correct function implementation can look the following way
int addlink( DataPtr *start, int element, double value )
{
DataPtr newPtr = malloc(sizeof(Data));
int success = newPtr != NULL;
if ( success )
{
newPtr->element = element;
newPtr->value = value;
newPtr->next = NULL;
while ( *start != NULL ) start = &( *start )->next;
*start = newPtr;
}
return success;
}
If you want to add new elements at the beginning of the list then the function can look like
int addlink( DataPtr *start, int element, double value )
{
DataPtr newPtr = malloc(sizeof(Data));
int success = newPtr != NULL;
if ( success )
{
newPtr->element = element;
newPtr->value = value;
newPtr->next = *start;
*start = newPtr;
}
return success;
}
As for showed by you the function implementation then it shall not even compile. For example in this code snippet
Data last = *start;
Data newPtr;
newPtr = malloc(sizeof(Data));
the expression *start has the type DataPtr while the initialized variable last has the type Data. The same problem exists for the variable newPtr.
The function print can look like
void print( DataPtr *start )
{
for ( DataPtr current = *start; current != NULL; current = current->next )
{
printf("%d ", current->element);
printf("%.3lf", current->value);
}
printf("\n");
}
And it is called like
print( &start );
This is a hashtable implementation.
I have the insert kinda working but how do I return the linked list?
I know that the remove is not done yet but I understand the concept, my problem is returning the adjusted list.
I tried to make the hashtable a global variable but the programming would force when I ran it.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
struct node {
char * data;
struct node * next;
};
struct hashtable {
struct node ** table;
int size;
int nentries;
};
struct hashtable * hashtable_new(int size) {
struct hashtable * result;
result = malloc(sizeof(struct hashtable));
result -> size = size;
result -> nentries = 0;
result -> table = malloc(sizeof(struct node) * size);
int i = 0;
for (i = 0; i < result->size; i++) {
result->table[i] = NULL;
// result->table[i]->data = NULL;
}
return result;
}
unsigned hash_string(struct hashtable *this, char * str) {
unsigned hash = 0;
int i = 0;
for ( i = 0; str[i] != '\0'; i++ ) {
hash = hash * 37 + str[i];
}
//return hash;
return hash % this-> size;
}
void hashtable_free(struct hashtable * this) {
int i;
struct node *table_nodes, *current, *next;
for(i = 0; i<this->size; i++) {
table_nodes = this->table[i];
current = table_nodes;
while (current != NULL){
next = current->next;
free(current);
current = next;
}
this->table[i] = NULL;
}
free(&this->table);
free(&this->size);
free(&this->nentries);
free(this);
}
void hashtable_insert(struct hashtable * table, char * string) {
struct node * new_node;
unsigned index = hash_string(table, string);
if(table->table[index] == NULL) {
printf("\nIndex: %d", index);
new_node = malloc(sizeof(struct node));
new_node -> next = table->table[index];
new_node -> data = string;
printf("\nData: %s", new_node->data);
table -> table[index] = new_node;
table -> nentries++;
printf("\n");
} else {
new_node = malloc(sizeof(struct node));
new_node->data = string;
new_node->next = NULL;
struct node * current = table->table[index];
struct node * next;
int size = 1;
while (current != NULL) {
next = current->next;
//if(current->data == string){
//return;
//}
if(current-> next == NULL){
//last element in list
current->next = new_node;
table->nentries++;
size++;
printf("\nIndex: %d", index);
printf("\nSize: %d", size);
printf("\nData: %s", current->next->data);
printf("\n");
return;
}
current = next;
size++;
}
}
}
void remove_hash(struct hashtable * this, char * item) {
//unsigned index = hash_string(this, item);
}
int lookup(struct hashtable * this, char * item) {
struct node *temp;
unsigned int index = hash_string(this, item);
temp = this->table[index];
while(temp != NULL) {
// do something
printf("%s, ", temp->data);
if(temp->data == item) {
printf("found %s\n", temp->data);
}
temp = temp->next;
}
return 0;
}
void print(struct hashtable * this) {
int i = 0;
printf("\n Size %d \n", this->size);
if(this == NULL) {
printf("Please construct the hashtable");
return;
}
for (i = 0; i < this->size; i++) {
if(this->table[i] == NULL) {
printf("\n %d: <empty>", i);
} else {
printf("\n %d: %s ", i, this->table[i]->data);
if(this->table[i]->next != NULL) {
printf("%s ", this->table[i]->next->data);
}
}
}
}
int main(int argc, char **argv) {
//struct node *theNode;
struct hashtable *theHash;
theHash = hashtable_new(9);
hashtable_insert(theHash, "I");
hashtable_insert(theHash, "am");
hashtable_insert(theHash, "a");;
hashtable_insert(theHash, "fish");
hashtable_insert(theHash, "glub");
print(theHash);
hashtable_insert(theHash, "glub");
lookup(theHash, "I");
print(theHash);
//printf("\n\n\n");
hashtable_free(theHash);
//print(theHash);
return 0;
}
Since C doesn't let you pass by reference, you can try returning the hashtable then reassigning your variable with the result of hashtable_insert:
struct hashtable *hashtable_insert(struct hashtable *table, char *string) {
// awesome code here
return current;
}
And then call it with:
theHash = hashtable_insert(theHash, "Wow!");