I'm trying to write a table-lookup package that would work for a symbol table management.
The names and replacement texts are held in a struct nlist type, and I have an array of pointers to the name and replacement text.
#define HASHSIZE 101
struct nlist { // table entry
struct nlist *next; // next entry in chain
char *name; // defined name
char *defn; // replacement text
};
static struct nlist *hashtab[HASHSIZE] = { NULL }; // pointer table
The lookup routine searches for s in the 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;
}
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
}
The install routine uses lookup to determine whether the name being installed is already present; if so, the new definition will supersede the old. Otherwise, a new entry is created.
// 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(np->defn); // free previous defn
if ((np->defn = strdup(defn)) == NULL)
return NULL;
return np;
}
In my main routine, "Access violation reading location" exception is thrown on the call of printf on pointer p. What is the cause of this error and how can I fix it?
int main()
{
struct nlist *install(char *name, char *defn);
struct nlist *p;
void undef(char *name);
p = install("DEFAULT", "ON");
printf("%s\n",p->name);
return 0;
}
I'm not certain of the correctness of the assignment of np within the install routine i.e. taking the size of *np instead of just struct nlist *.
Additionally, I have read advice that the additional cast isn't necessary.
struct nlist *np;
...
np = (struct nlist *) malloc(sizeof(*np));
PICNIC. I didn't include <string.h>
Related
I'm trying to implement a key-value hash table and i've already got a couple of functions that i've found, one that inserts an element and another one that looks up an element with a given key, here's the code:
#define HASHSIZE 101
static key_value *hashtab[HASHSIZE]; /* pointer table */
unsigned hash(char *s);
typedef struct key_value
{ /* table entry: */
struct key_value *next; /* next entry in chain */
char *key; /* defined name */
char *value; /* replacement text */
} key_value;
/* 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 */
key_value *lookup(char *s)
{
key_value *np;
for (np = hashtab[hash(s)]; np != NULL; np = np->next)
if (strcmp(s, np->key) == 0)
return np; /* found */
return NULL; /* not found */
}
/* install: put (key, value) in hashtab */
key_value *install(char *key, char *value)
{
key_value *np;
unsigned hashval;
if ((np = lookup(key)) == NULL)
{ /* not found */
np = (key_value *)malloc(sizeof(*np));
if (np == NULL || (np->key = strdup(key)) == NULL)
return NULL;
hashval = hash(key);
np->next = hashtab[hashval];
hashtab[hashval] = np;
}
else /* already there */
free((void *)np->value); /*free previous value */
if ((np->value = strdup(value)) == NULL)
return NULL;
return np;
}
I now would like to make a couple of functions, one that will print an hash table in its entirety, and another one that will delete a member from that hash table with a given key. Any help would be deeply appreciated.
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;
I have read this article: Quick Way to Implement Dictionary in C and implemented it. Need to say that I change the install and lookup to use hashtab as a argument instead of a global variable, It worked like charm:
struct nlist *install(struct nlist **hashtab,char *name, symbol *dfn);
struct nlist *lookup(struct nlist **hashtab, char *s);
The problem is, I need to copy the hash into a new one. I first try:
struct nlist **copy_context(struct nlist **hashtab){
nlist **copy = (nlist**)malloc(sizeof(nlist*)*HASHSIZE);
struct nlist *np;
int i = 0;
for (np = hashtab[i]; i<HASHSIZE ;i++ ){
debug_log("i vale %i",i);
if( np != NULL ){
install(copy,np->name,np->dfn);
}
}
return copy;
}
but is not clear to me where the values are stored neither where to start the copy. I was thinking something like:
struct nlist **copy_context(struct nlist **hashtab){
nlist **copy = (nlist**)malloc(sizeof(nlist*)*HASHSIZE);
i = 0;//this is not correct
or (np = hashtab[i]; np != NULL; np = np->next){
install(copy,np->name,np->dfn);
}
return copy;
}
but is notingh is working.
as "Some Programmer dude" said I need to do two loops, one on the table itself and one for the list in that table index.
struct nlist **copy_context(struct nlist **hashtab){
nlist **copy = (nlist**)malloc(sizeof(nlist*)*HASHSIZE);
struct nlist *np;
for (int i = 0; i<HASHSIZE; i++ ){
np = hashtab[i];
while( np )
{
install(copy,np->name,np->dfn);
np = np->next;
}
}
return copy;
}
here is the install function from the hash tables example from K&R's book:
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;
}
I don't understand the line np->next = hashtab[hasvall]
I thought the reason to have the variable np->next is for putting in the table two string with the same hash value, but the outcome from this is having only one name for every hash value.
Furthermore I cannot seem to understand the function lookup, and the "AFTERTHOUGHT part in the for(because I think there is only one vaule to every struct in the talbe:
/* 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 */
}
What am I missing?
You can have only one key (name) for every value, but two or more keys can have the same hash. np->next = hashtab[hashval] adds the new hashval to the linked list. Lookup then iterates through the list until the key (name) is matched.
np->next = hashtab[hashval];
hashtab[hashval] = np;
These two lines do not replace the old entry, they add to it.
hashtab[hashval]-> existing_node becomes
hashtab[hashval]-> np -(next)-> existing_node
As #Bo Persson mentions in the comments, this is called "chaining".
Given this structure, the lookup function correctly checks the names of each node in the chain.
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);
}