Segmentation fault when installing key value in hash table - c

EDIT: I have updated the init function(code updated) to use malloc and the segmentation fault is gone. However I get no output from the print table function now.
Further updated the code as per suggestions. It seems to work now.
I've been following K&R (beginner in C) for C and tried writing a hashtable using their example in section 6.7 (with few modifications)
The code is below-
#include <stdio.h>
#include <string.h>
#include "hashtable.h"
#define HASHSIZE 101
listptr * init_table()
{
listptr *hashtab = (listptr *) calloc(HASHSIZE, sizeof(*hashtab));
return hashtab;
}
unsigned hash (char *s)
{
unsigned hashval;
for (hashval=0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
listptr lookup (listptr * hashtab, char *s)
{
listptr np;
for (np = hashtab[hash(s)]; np!=NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np;
return NULL;
}
listptr install(listptr * hashtab, char *name, char * defn)
{
listptr np;
unsigned hashval;
if((np = lookup(hashtab, name)) == NULL) {
np = (listptr) malloc(sizeof(*np));
if (np==NULL || (np->name = strdup(name))==NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
}
else
{
free((void*) np->defn);
}
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
void printtable(listptr * table, int len)
{
listptr p;
int i =0;
while (i < len) {
if (table[i] != NULL) {
for (p = table[i];p!=NULL;p=p->next) {
printf("%s\t%s\n", p->name, p->defn);
}
}
i++;
}
}
hashtable.h contains -
#ifndef HDR
#define HDR
#include <stdlib.h>
typedef struct nlist *listptr;
typedef struct nlist {
listptr next;
char *name;
char *defn;
} Hashtablebucket;
listptr * init_table();
listptr lookup(listptr *, char *);
listptr install (listptr *, char *, char *);
void printtable(listptr *, int );
#endif
In main.c I have -
#include <stdio.h>
#include <string.h>
#include "hashtable.h"
int main()
{
listptr * table = init_table();
install(table, "key1", "value1");
install(table, "key2", "value2");
install(table, "key3", "value3");
printtable(table, 101);
return 0;
}
This results in a segmentation fault and I have no idea what could be wrong as the hashtable has 101 elements of space.
Would appreciate any help in debugging the problem...
EDIT: With the above code there is no output at all. Could someone please help with the debug?
Thanks in advance

The original K&R code assumes a global table. In your case you try to allocate it locally, but you cannot return a pointer to local variable (well, you can, but the behaviour is undefined). Instead you need to allocate the memory using malloc/or even better, calloc in this case:
listptr * init_table()
{
listptr *table = calloc(HASHSIZE, sizeof *table);
return table;
}
It would be preferable to make a struct for the hash table, so that you can have tables of different sizes:
struct hashtable {
size_t n_slots;
listptr *slots;
};
struct hashtable *init_table(size_t n_slots) {
struct hashtable *tbl = malloc(sizeof *tbl);
tbl->n_slots = n_slots;
tbl->slots = calloc(n_slots, sizeof *(tbl->slots));
return tbl;
}
For hash function, it is better to keep it so that it returns an unsigned int (or size_t!) always, and do the modulo outside that function. Also, char can be signed or unsigned; you'd most probably want to use unsigned chars.
I.e.
size_t hash (char *s)
{
size_t hashval;
for (hashval=0; *s != '\0'; s++)
hashval = *(unsigned char*)s + 31 * hashval;
return hashval;
}
and
hashval = hash(name) % tbl->n_slots;

Related

Hashtable with linked list not work in c?

I've a problem with memory allocation for an hash table with linked list (for avoid collisions) in C.
I think that the problem is on allocation of an item.
I've made two scruct, one for the single item and one for the table.
The first have two pointer to next and prev item.
Please help me.
I stay on this code until 3 days.
The code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CAPACITY 50000
unsigned long hash(char *str) {
unsigned long int stringsum = 0;
for(; *str != '\0'; str++) {
stringsum += *str;
}
return stringsum % CAPACITY;
}
typedef struct item {
char *value;
char *key;
struct item *next;
struct item *prev;
} ht_item;
typedef struct hashtable {
ht_item **items;
int dim;
int count;
} HashTable;
HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key);
void print_table(HashTable* table, int dim);
int main(void) {
HashTable *table = create_table(CAPACITY);
table = create_item(table, "Giuseppe", "Nome");
print_table(table, CAPACITY);
return 0;
}
HashTable* create_item(HashTable *table, char *value, char *key) {
unsigned long index = hash(key);
printf("%u", index);
ht_item *_iterator; ht_item *prev;
for(_iterator = table->items[index], prev = NULL; _iterator != NULL; prev = _iterator, _iterator = _iterator->next);
_iterator = (ht_item*)malloc(sizeof(ht_item));
_iterator->key = (char*)malloc(200);
_iterator->value = (char*)malloc(200);
strcpy(_iterator->key, key);
strcpy(_iterator->value, value);
_iterator->next = NULL;
_iterator->prev = prev;
return table;
}
HashTable* create_table(int size)
{
HashTable *table = (HashTable*)malloc(sizeof(HashTable));
table->dim = size;
table->items = (ht_item**)calloc(size, sizeof(ht_item*));
for(int i = 0; i < size; i++){
table->items[i] = NULL;
}
return table;
}
void print_table(HashTable* table, int dim) {
for(int i = 0; i < CAPACITY; i++)
{
if(table->items[i] != NULL)
{ ht_item *_iterator = (ht_item*)malloc(sizeof(ht_item));
for(_iterator = table->items[i]; _iterator != NULL;
_iterator = _iterator->next)
{
printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value);
} free(_iterator);
}
}
}
Made some changes in your code. Please read through the blocks containing // CHANGE HERE comment.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CAPACITY 50000
// CHANGE HERE - additional parameter, value to be used for modulo
unsigned long hash(char *str, unsigned int mod_value) {
unsigned long int stringsum = 0;
for(; *str != '\0'; str++) {
stringsum += *str;
}
// CHANGE HERE - use mod_value instead of CAPACITY
return stringsum % mod_value;
}
typedef struct item {
char *value;
char *key;
struct item *next;
struct item *prev;
} ht_item;
typedef struct hashtable {
ht_item **items;
int dim;
int count;
} HashTable;
HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key);
void print_table(HashTable* table, int dim);
int main(void) {
HashTable *table = create_table(CAPACITY);
table = create_item(table, "Giuseppe", "Nome");
print_table(table);
return 0;
}
HashTable* create_item(HashTable *table, char *value, char *key) {
// CHANGE HERE - function arguments validation
if (table == NULL)
{
return table;
}
if (value == NULL || key == NULL)
{
printf("Key or value is null\n");
return table;
}
// CHANGE HERE - pass table->dim to hash
unsigned long index = hash(key, table->dim);
printf("Index: %lu\n", index);
// CHANGE HERE - simplified the code a bit
ht_item* new_node = malloc(sizeof(ht_item));
new_node->key = malloc(200 * sizeof(char));
strncpy(new_node->key, key, 200);
new_node->value = malloc(200 * sizeof(char));
strncpy(new_node->value, value, 200);
// CHANGE HERE - if first node in index
if (table->items[index] == NULL)
{
table->items[index] = new_node;
return table;
}
ht_item *cur, *prev = NULL;
for(cur = table->items[index]; cur != NULL; prev = cur, cur = cur->next);
prev->next = new_node; // CHANGE HERE - it seems this line was missing
new_node->prev = prev;
new_node->next = NULL;
return table;
}
HashTable* create_table(int size)
{
HashTable *table = (HashTable*)malloc(sizeof(HashTable));
table->dim = size;
table->items = (ht_item**)calloc(size, sizeof(ht_item*));
for(int i = 0; i < size; i++){
table->items[i] = NULL;
}
return table;
}
void print_table(HashTable* table) {
// CHANGE HERE - function arguments validation
if (table == NULL)
{
printf("Table is null\n");
return;
}
// CHANGE HERE - change CAPACITY to dim
for(int i = 0; i < table->dim; i++)
{
//printf("i = %d [%d]\n", i, table->items[i] == NULL);
if(table->items[i] != NULL)
{
// CHANGE HERE - removed unnecessary malloc
ht_item *_iterator = NULL;
for(_iterator = table->items[i]; _iterator != NULL; _iterator = _iterator->next)
{
printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value);
}
}
}
}
The create_item function can and should be simplified.
I have put some comments inline.
HashTable* create_item(HashTable *table, char *value, char *key) {
// use modulo operator here, not in the hash function
unsigned long index = hash(key) % table->dim;
// nicer way of allocating
ht_item *insert = malloc(sizeof *insert);
// use strdup to avoid wasted memory and buffer overflows
insert->key = strdup(key);
insert->value = strdup(value);
// head insert rather than tail
insert->next = table->items[index];
table->items[index] = insert;
return table;
}
I dropped the use of the prev member. If you need that somewhere it's an exercise for you to add it. I don't think it's necessary for a simple hash table.

Hashtable leaking memory

I made a hashtable, and it runs fine, but when i run it with valgrind,it tells me there is memory leaks when creating the hashtable and a memory leak for every user insertion, 12 bytes per insertion and 40 for creating the hashtable
here is some testable code:
#include <malloc.h>
#include <stdio.h>
#include "string.h"
#include "stdlib.h"
#define initial_size 5
typedef struct user_pos {
char nick[6];
int position_in_file;
}user_pos;
typedef struct bucket{
user_pos *info;
}bucket;
typedef struct hashtable{
int size;
int elements;
bucket **buckets;
}hashtable;
unsigned hash(char *s) {
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31*hashval;
return hashval;
}
hashtable * create() {
hashtable *htable;
if((htable = malloc(sizeof(hashtable))) == NULL)
return NULL;
if((htable->buckets = malloc(sizeof(bucket) * initial_size)) == NULL)
return NULL;
htable->size = initial_size;
htable->elements = 0;
for(int i=0; i < initial_size; i++){
htable->buckets[i] = NULL;
}
return htable;
}
void insert(hashtable *hashtable, char *nick, int position_in_file){
int hash_value = hash(nick);
int new_position = hash_value % hashtable->size;
if (new_position < 0) new_position += hashtable->size;
int position = new_position;
while (hashtable->buckets[position] != NULL && position != new_position - 1) {
if(hashtable->buckets[position]->info != NULL){
position++;
position %= hashtable->size;
}else{
break;
}
}
hashtable->buckets[position] = malloc(sizeof(bucket));
hashtable->buckets[position]->info = malloc(sizeof(user_pos));
strcpy(hashtable->buckets[position]->info->nick, nick);
hashtable->buckets[position]->info->position_in_file = position_in_file;
hashtable->elements++;
}
void delete_hashtable(hashtable *ht) {
for(int i = 0; i<ht->size; i++){
if(ht->buckets[i] != NULL && ht->buckets[i]->info != NULL)
free(ht->buckets[i]);
}
free(ht);
}
int main(){
hashtable *ht = create();
insert(ht, "nick1", 1);
insert(ht, "nick2", 2);
delete_hashtable(ht);
return 0;
}
I am initializing a bucket everytime i insert a new item, but i think i cant free it after, because that would erase what was just added, the same for the create() function.
How do i avoid leaking memory in this case?
Thanks in advance.
The memory leak is not in your allocation function but in the cleanup. You fail to free everything you allocated in delete_hashtable.
You clean up ht->buckets[i] and ht, but fail to clean up ht->buckets[i]->info and ht->buckets. You need to free those as well:
void delete_hashtable(hashtable *ht) {
for(int i = 0; i<ht->size; i++){
if(ht->buckets[i] != NULL && ht->buckets[i]->info != NULL)
free(ht->buckets[i]->info);
free(ht->buckets[i]);
}
free(ht->buckets);
free(ht);
}
Also, you're allocating the wrong number of bytes for htable->buckets:
if((htable->buckets = malloc(sizeof(bucket) * initial_size)) == NULL)
Each element is a bucket *, not a bucket, so that's the size you should use:
if((htable->buckets = malloc(sizeof(bucket *) * initial_size)) == NULL)
Or better yet:
if((htable->buckets = malloc(sizeof(*htable->buckets) * initial_size)) == NULL)
That way it doesn't matter what the type of htable->buckets is or if you change it later.

How preprocessor (i.e.#define) works? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have the basic knowledge of preprocessor.But i have never understood how it works.Can some body explain me how it works?I would prefer practical explanation.Thank you.
#define A 1234
Literally as far as the compiler is concerned, every instance of A is now 1234.
Its useful for code readability and modifieability. If you have a constant number for instance thats used in 7-8 different functions, you can #define it and use a token instead, and then change it in 1 place rather then 7-8.
Alternatives to using #define are using constant variables or enums.
#define myVar 755
const int myVar = 755;
enum{ myVar = 755 };
Are all functionally equivalent in most cases.
#define MY_MACRO my_var
Everytime your compilator finds "MY_MACRO" in your code while creating the binary code, it considers you wrote "my_var".
In one of my assignment i have implemented simple pre-processor using c.I have used the simple hash table to store the name and its definition.
for example,
#define a 4
so here a is name and 4 is its definition.
So both a and 4 will be stored in the hash table.
now whenever in program name 'a' is encountered then it will be simply replaced by 4.
i am posting my code in c for pre-processor.Which is very simple.But it would be very helpful for you to understand how pre-processor works.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define HASHSIZE 101
#define BUFSIZE 100
#define MAXWORD 100
struct tnode {
int loc[10]; // to store the locations at which words occur
int i; // to store the index of array loc
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
static struct nlist *hashtab[HASHSIZE];
int getword(char *word, int lim);
struct tnode *addtree(struct tnode *, char *);
void treeprint(struct tnode *);
struct tnode *talloc(void);
struct nlist {
/* table entry: */
struct nlist *next;
/* next entry in chain */
char *name;
/* defined name */
char *defn;
/* replacement text */
};
char buf[BUFSIZE];
int bufp = 0;
unsigned hash(char *s);
struct nlist *lookup(char *s);
char *strdup1(char *);
struct nlist *install(char *name, char *defn);
int undef(char *s); // this function will remove entry
struct tnode *z[10]; // to store address of each node(word)
int total = 0; // index for array z
char s1[1000]; // to store entire input
static int q = 0; // index to s1
int main() {
struct nlist *pt;
char word[100];
char name1[10];
char defn1[10];
int i = 0;
int k = 0;
/*
#define pi 5 is entered then...*/
while (getword(word, MAXWORD) != EOF) {
if (isalnum(word[0]) && (i == 0)) {
if ((pt = lookup(&word[0])) !=
NULL) // if name1(pi) is already present then replace it with its definition name(5)
{
strcpy(word, (pt->defn)); // if pi is alerady there then replace name by its definition
// strcpy((pt->defn),word);
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
} else // otherwise print the word
{
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
}
}
if (isalnum(word[0])) {
if (i == 2) // copy word to defn1(i.e. defn1=5)
{
strcpy(defn1, word);
install(name1, defn1);
i = 0;
}
}
if (isalnum(word[0])) {
if (i == 1) // copy the word to name1(i.e. name1=pi)
{
strcpy(name1, word);
i++;
}
}
if (word[0] == '#') {
if ((strcmp(word, "#define")) == 0) // if #define found then increase i to get next word(i.e. pi)
{
i++;
} else {
for (k = 0; word[k] != '\0'; k++, q++) {
s1[q] = word[k];
}
}
}
}
// printf("%d",i);
// printf("%s %s",name,defn);
// printf("%s",pt->defn);
printf("%s", s1);
}
unsigned hash(char *s) {
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
/* lookup: look for s in hashtab */
struct nlist *lookup(char *s) {
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np;
/* found */
return NULL;
/* not found */
}
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn) {
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *)malloc(sizeof(*np));
if (np == NULL || (np->name = strdup1(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else
/* already there */
free((void *)np->defn);
/*free previous defn */
if ((np->defn = strdup1(defn)) == NULL)
return NULL;
return np;
}
char *strdup1(char *s) {
char *p;
/* make a duplicate of s */
p = (char *)malloc(strlen(s) + 1); /* +1 for '\0' */
if (p != NULL)
strcpy(p, s);
return p;
}
int undef(char *s) {
struct nlist *np1;
if ((np1 = lookup(s)) != NULL) {
hashtab[hash(s)] = NULL; // delete the entry in array hashtab associated to s
free(np1); // free memory allocated to s
return 1; // if sucessful then return 1 else 0
}
return 0;
}
int getword(char *word, int lim) {
int c, getch(void);
void ungetch(int);
char *w = word;
while (isspace(c = getch())) {
s1[q] = ' ';
q++;
} // takes the space in string s1
if (c != EOF)
*w++ = c;
if (!isalnum(c) && (c != '#')) /*takes all character except #,digit and number in s*/
{
if (c != EOF) {
s1[q] = c;
q++;
}
*w = '\0';
return c;
}
for (; --lim > 0; w++)
if (!isalnum(*w = getch())) {
ungetch(*w);
break;
}
*w = '\0';
return word[0];
}
int getch(void) /* get a (possibly pushed-back) character */
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
/* push character back on input */
{
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
/* addtree: add a node with w, at or below p */
struct tnode *addtree(struct tnode *p, char *w) {
int cond;
if (p == NULL) {
/* a new word has arrived */
p = talloc();
/* make a new node */
p->word = strdup(w);
p->count = 1;
p->left = p->right = NULL;
} else if ((cond = strcmp(w, p->word)) == 0)
p->count++;
/* repeated word */
else if (cond < 0)
/* less than into left subtree */
p->left = addtree(p->left, w);
else
/* greater than into right subtree */
p->right = addtree(p->right, w);
return p;
}
/* talloc: make a tnode */
struct tnode *talloc(void) {
return (struct tnode *)malloc(sizeof(struct tnode));
}
void treeprint(struct tnode *p) {
int i = 0;
for (i = 0; i < total; i++)
printf("%s", z[i]->word);
}
The code is self explanatory.Mind you it is very simple pre-processor.
The purpose of a #define is to give a value or macro a simple name that you can use in your program. This is called a symbolic constant. In stead of having to remember the value or retype the macro everywhere you need it, you define it once and use the name wherever you need. Also, to define for example the size of something (array, buffer), you define it once. Should you later need to change the size, you only need to edit the definition. These are only some examples of #defines.
The preprocessor reads the program text and replaces the macros and constants with their actual values. If a part of the program text is enclosed in #ifdef...#endif (conditional compilation) and the #ifdef evalates to FALSE, then that part is left out. In the next step the actual compiler compiles the resulting program text into actual code.

C - How to setup an associative array of function pointers?

Here's what I'm trying to do:
myValueType function1(myParam){}
myValueType function2(myParam){}
myArray[CONSTANT_STATE1] = &function1;
myArray[CONSTANT_STATE2] = &function2;
myValue = (*myArray[CONSTANT_STATE1])(myParam);
When I compile, it throws an error that I've redeclared function1.
What's the best way to do this?
As per this SO answer from user Vijay Mathew:
Section 6.6 of The C Programming Language presents a simple dictionary (hashtable) data structure. I don't think a useful dictionary implementation could get any simpler than this. For your convenience, I reproduce the code here.
struct nlist { /* table entry: */
struct nlist *next; /* next entry in chain */
char *name; /* defined name */
char *defn; /* replacement text */
};
#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */
/* hash: form hash value for string s */
unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != ’\0’; s++)
hashval = *s + 31 * hashval;
return hashval % HASHSIZE;
}
/* lookup: look for s in hashtab */
struct nlist *lookup(char *s)
{
struct nlist *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->name) == 0)
return np; /* found */
return NULL; /* not found */
}
char *strdup(char *);
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn)
{
struct nlist *np;
unsigned hashval;
if ((np = lookup(name)) == NULL) { /* not found */
np = (struct nlist *) malloc(sizeof(*np));
if (np == NULL || (np->name = strdup(name)) == NULL)
return NULL;
hashval = hash(name);
np->next = hashtab[hashval];
hashtab[hashval] = np;
} else /* already there */
free((void *) np->defn); /*free previous defn */
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
char *strdup(char *s) /* make a duplicate of s */
{
char *p;
p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */
if (p != NULL)
strcpy(p, s);
return p;
}
Note that if the hashes of two strings collide, it may lead to an O(n) lookup time. You can reduce the likely hood of collisions by increasing the value of HASHSIZE. For a complete discussion of the data structure, please consult the book.
The code you've shown is almost right. The problem is in your function declarations:
myValueType function1(myParam){}
myValueType function2(myParam){}
These are old-style K&R non-prototyped declarations - the name of the parameter is myParam, and the type has not been specified. Perhaps you meant this?
myValueType function1(myParamType myParam){}
myValueType function2(myParamType myParam){}
Expanding your code out to a minimal compilable example:
typedef int myValueType, myParamType;
enum { CONSTANT_STATE1, CONSTANT_STATE2 };
myValueType function1(myParamType myParam){}
myValueType function2(myParamType myParam){}
void f(myParamType myParam)
{
myValueType myValue;
myValueType (*myArray[2])(myParamType);
myArray[CONSTANT_STATE1] = &function1;
myArray[CONSTANT_STATE2] = &function2;
myValue = (*myArray[CONSTANT_STATE1])(myParam);
}

Heap error in DLL

I'm looking for some help with some C dll programming. I am getting an error in Visual Studio that occurs when I call free from within the dll. The program runs fine in debug mode within the IDE, but when I try to execute it as "Start without debugging", the program crashes. I read that with debugging, the heap is shared, which probably explains why the code runs fine with F5 and not Ctrl-F5. Is this correct??
I've searched around, and I learned that it is dangerous to pass dynamically allocated memory through the dll-exe boundary, however as I am calling malloc and free from within the same dll, this should not be a problem. I have posted the code below. Any help would be greatly appreciated. Please note that this code works on my Linux box, I am simply trying to port it to Windows to garner experience programming in Windows.
Thank you
#ifndef HASHTABLE_H
#define HASHTABLE_H
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the HASHTABLE_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// HASHTABLE_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef HASHTABLE_EXPORTS
#define HASHTABLE_API __declspec(dllexport)
#else
#define HASHTABLE_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* This is a naive implementation
* of a hashtable
*/
typedef struct node {
struct node *next;
char *value;
char *key;
} Node;
typedef struct hashtable {
struct node **nodes;
int num_elements;
int size;
int (*hash_function)(const char * const);
} Hashtable_str;
// Construction and destruction
HASHTABLE_API void tbl_construct(Hashtable_str **table);
HASHTABLE_API void tbl_destruct(Hashtable_str *table);
// Operations
HASHTABLE_API int tbl_insert (Hashtable_str *table, const char * const key, const char * const element); // return the key
HASHTABLE_API int tbl_remove(Hashtable_str *table, const char * const key);
HASHTABLE_API char * tbl_find(Hashtable_str *table, const char * const key); // return the element
HASHTABLE_API int size(Hashtable_str *table); // Return the size
// default hash function
int def_hash(const char * const key);
#ifdef __cplusplus
}
#endif
#endif
Here is the implementation code
// hashtable.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "hashtable.h"
#include <stdlib.h> // for memcpy
#include <stdio.h>
#include <string.h>
#define SIZE 100
int def_hash(const char * const key)
{
// simply sum the ascii
// values and take modulo
// 100
//int i;
//int sum;
//sum = 0;
//for (i=0; key[i] != '\0'; i++)
// sum += key[i];
//return sum % SIZE;
return 0;
}
// construct a hashtable and return a pointer
HASHTABLE_API void tbl_construct(Hashtable_str **tbl)
{
int i;
Hashtable_str *tbl_ptr;
*tbl = (Hashtable_str*) malloc (sizeof(Hashtable_str*));
tbl_ptr = *tbl;
tbl_ptr->nodes = (Node**) malloc (SIZE * sizeof(Node*));
for (i=0; i < SIZE; i++) tbl_ptr->nodes[i] = NULL;
tbl_ptr->hash_function = &def_hash;
tbl_ptr->num_elements = 0;
tbl_ptr->size = SIZE;
}
HASHTABLE_API void tbl_destruct(Hashtable_str *tbl)
{
void free_tbl_node(Node*); // declare the release function
int i;
for (i=0; i < tbl->size; i++)
{
if (tbl->nodes[i] != NULL)
free_tbl_node(tbl->nodes[i]);
}
}
void free_tbl_node(Node *curr_node)
{
if (curr_node->next != NULL)
free_tbl_node(curr_node->next);
free(curr_node->value);
curr_node->value = NULL;
free(curr_node->key);
curr_node->key = NULL;
//free(curr_node);
//Node *temp = NULL;
//Node *temp2 = NULL;
//temp = temp2 = curr_node;
//while (temp->next != NULL)
//{
// temp2=temp->next;
// free(temp->key);
// free(temp->value);
// free(temp);
// temp=temp2;
//}
}
// table operations
HASHTABLE_API int count(Hashtable_str *tbl) { return tbl->num_elements; }
HASHTABLE_API int size(Hashtable_str *tbl) { return tbl->size; }
HASHTABLE_API int tbl_insert(Hashtable_str *table, const char * const key, const char * const element)
{
int hash;
Node *temp_ptr = NULL;
hash = table->hash_function(key);
// printf("Placing into column %d\n", hash);
if (table->nodes[hash] == NULL)
{
table->nodes[hash] = (Node*) malloc(sizeof(Node*));
temp_ptr = table->nodes[hash];
temp_ptr->next = NULL;
temp_ptr->key = (char*) malloc (strlen(key) + 1 * sizeof(char));
temp_ptr->value = (char*) malloc (strlen(element) + 1 * sizeof(char));
strcpy_s(temp_ptr->key, strlen(key)+1, key);
strcpy_s(temp_ptr->value, strlen(element)+1, element);
table->num_elements += 1;
}
else
{
// Collision!!
temp_ptr = table->nodes[hash];
while (temp_ptr->next != NULL)
temp_ptr = temp_ptr->next;
temp_ptr->next = (Node*) malloc(sizeof(Node));
temp_ptr->next->key = (char*) malloc (strlen(key)+1 * sizeof(char));
temp_ptr->next->value = (char*) malloc (strlen(element)+1 * sizeof(char));
temp_ptr->next->next = NULL;
strcpy_s(temp_ptr->next->key, strlen(key)+1, key);
strcpy_s(temp_ptr->next->value, strlen(element)+1, element);
table->num_elements += 1;
}
// Return the hash value itself for hacking
return hash;
}
HASHTABLE_API int tbl_remove(Hashtable_str *tbl, const char * const key)
{
int hash;
Node *temp_ptr = NULL;
Node *chain = NULL;
hash = tbl->hash_function(key);
if (tbl->nodes[hash] == NULL)
return 1;
else
{
temp_ptr = tbl->nodes[hash];
if (strcmp(key, temp_ptr->key) == 0)
{
// The next node is the node in question
chain = temp_ptr->next;
free(temp_ptr->value);
printf("Deleted the value\n");
free(temp_ptr->key);
printf("Deleted the key\n");
//printf("About to delete the node itself\n");
//free(temp_ptr);
tbl->nodes[hash] = chain;
tbl->num_elements -= 1;
return 0;
}
else
{
while (temp_ptr->next != NULL)
{
if (strcmp(key, temp_ptr->next->key) == 0)
{
// The next node is the node in question
// So grab a pointer to the node after it
// and remove the next node
chain = temp_ptr->next->next;
free(temp_ptr->next->key);
free(temp_ptr->next->value);
//free(temp_ptr->next);
temp_ptr->next = chain;
tbl->num_elements -= 1;
return 0;
}
else
temp_ptr = temp_ptr->next;
}
}
// Couldn't find the node, so declare not existent
return 1;
}
}
HASHTABLE_API char * tbl_find(Hashtable_str *tbl, const char * const key)
{
// Compute the hash for the index
int hash;
Node *temp_ptr = NULL;
hash = tbl->hash_function(key);
if (tbl->nodes[hash] == NULL)
return NULL;
else
{
temp_ptr = tbl->nodes[hash];
if (strcmp(key, temp_ptr->key) != 0)
{
while (temp_ptr->next != NULL)
{
temp_ptr = temp_ptr->next;
if (strcmp(key, temp_ptr->key) == 0)
return temp_ptr->value;
}
}
// Couldn't find the node, so declare not existent
return NULL;
}
}
Here's my main
#include <hashtable.h>
#include <utils.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i=0;
Hashtable_str *my_table = NULL;
tbl_construct(&my_table);
tbl_insert(my_table, "Daniel", "Student");
tbl_insert(my_table, "Derek", "Lecturer");
//tbl_insert(my_table, "Melvyn", "Lecturer");
tbl_print(my_table);
printf("\nRemoving Daniel...\n");
tbl_remove(my_table, "Daniel");
//tbl_print(my_table);
tbl_destruct(my_table);
my_table = NULL;
scanf_s("%d", &i);
return 0;
}
This is incorrect as allocates the size of a pointer, not a Hashtable_str:
*tbl = (Hashtable_str*) malloc (sizeof(Hashtable_str*));
it should be:
*tbl = malloc(sizeof(Hashtable_str));
Same issue with:
table->nodes[hash] = (Node*) malloc(sizeof(Node*));
See Do I cast the result of malloc?

Resources