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.
Related
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>
I'm currently working on a hash table implementation in C. I'm trying to implement dynamic resizing, but came across a problem.
If resizing a hash table means creating a new one with double (or half) the size, rehashing, and deleting the old one, how can I deal with old references the user may have made to the old table? Example code (I've omitted error checking just for this example):
int main(int argc, char *argv[])
{
ht = ht_create(5) /* make hashtable with size 5 */
ht_insert("john", "employee"); /* key-val pair "john -> employee" */
ht_insert("alice", "employee");
char *position = ht_get(ht, "alice"); /* get alice's position from hashtable ht */
ht_insert("bob", "boss"); /* this insert exceeds the load factor, resizes the hash table */
printf("%s", position); /* returns NULL because the previous hashtable that was resized was freed */
return 0;
}
In this case position pointed to alice's value which was found in the hashtable. When it was resized, we freed the hash table and lost it. How can I fix this problem, so the user won't have to worry that a previously defined pointer was freed?
EDIT: my current hash table implementation
hash.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash.h"
#define LOADFACTOR 0.75
typedef struct tableentry /* hashtab entry */
{
struct tableentry *next;
char *key;
void *val;
} tableentry_t;
typedef struct hashtable
{
datatype_t type;
size_t size;
size_t load; /* number of keys filled */
struct tableentry **tab;
} hashtable_t;
/* creates hashtable */
/* NOTE: dynamically allocated, remember to ht_free() */
hashtable_t *ht_create(size_t size, datatype_t type)
{
hashtable_t *ht = NULL;
if ((ht = malloc(sizeof(hashtable_t))) == NULL)
return NULL;
/* allocate ht's table */
if ((ht->tab = malloc(sizeof(tableentry_t) * size)) == NULL)
return NULL;
/* null-initialize table */
size_t i;
for (i = 0; i < size; i++)
ht->tab[i] = NULL;
ht->size = size;
ht->type = type;
return ht;
}
/* creates hash for a hashtab */
static unsigned hash(char *s)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval;
}
static int *intdup(int *i)
{
int *new;
if ((new = malloc(sizeof(int))) == NULL)
return NULL;
*new = *i;
return new;
}
static void free_te(tableentry_t *te)
{
free(te->key);
free(te->val);
free(te);
}
/* loops through linked list freeing */
static void free_te_list(tableentry_t *te)
{
tableentry_t *next;
while (te != NULL)
{
next = te->next;
free_te(te);
te = next;
}
}
/* creates a key-val pair */
static tableentry_t *alloc_te(char *k, void *v, datatype_t type)
{
tableentry_t *te = NULL;
int status = 0;
/* alloc struct */
if ((te = calloc(1, sizeof(*te))) == NULL)
status = -1;
/* alloc key */
if ((te->key = strdup(k)) == NULL)
status = -1;
/* alloc value */
int *d;
char *s;
switch (type)
{
case STRING:
s = (char *) v;
if ((te->val = strdup(s)) == NULL)
status = -1;
break;
case INTEGER:
d = (int *) v;
if ((te->val = intdup(d)) == NULL)
status = -1;
break;
default:
status = -1;
}
if (status < 0)
{
free_te_list(te);
return NULL;
}
te->next = NULL;
return te;
}
static tableentry_t *lookup(hashtable_t *ht, char *k)
{
tableentry_t *te;
/* step through linked list */
for (te = ht->tab[hash(k) % ht->size]; te != NULL; te = te->next)
if (strcmp(te->key, k) == 0)
return te; /* found */
return NULL; /* not found */
}
/* inserts the key-val pair */
hashtable_t *ht_insert(hashtable_t *ht, char *k, void *v)
{
tableentry_t *te;
/* unique entry */
if ((te = lookup(ht, k)) == NULL)
{
te = alloc_te(k, v, ht->type);
unsigned hashval = hash(k) % ht->size;
/* insert at beginning of linked list */
te->next = ht->tab[hashval];
ht->tab[hashval] = te;
ht->load++;
}
/* replace val of previous entry */
else
{
free(te->val);
switch (ht->type)
{
case STRING:
if ((te->val = strdup(v)) == NULL)
return NULL;
break;
case INTEGER:
if ((te->val = intdup(v)) == NULL)
return NULL;
break;
default:
return NULL;
}
}
return ht;
}
static void delete_te(hashtable_t *ht, char *k)
{
tableentry_t *te, *prev;
unsigned hashval = hash(k) % ht->size;
te = ht->tab[hashval];
/* point head to next element if deleting head */
if (strcmp(te->key, k) == 0)
{
ht->tab[hashval] = te->next;
free_te(te);
ht->load--;
return;
}
/* otherwise look through, keeping track of prev to reassign its ->next */
for (; te != NULL; te = te->next)
{
if (strcmp(te->key, k) == 0)
{
prev->next = te->next;
free_te(te);
ht->load--;
return;
}
prev = te;
}
}
hashtable_t *ht_delete(hashtable_t *ht, char *k)
{
size_t i;
if (lookup(ht, k) == NULL)
return NULL;
else
delete_te(ht, k);
}
/* retrieve value from key */
void *ht_get(hashtable_t *ht, char *k)
{
tableentry_t *te;
if ((te = lookup(ht, k)) == NULL)
return NULL;
return te->val;
}
/* frees hashtable created from ht_create() */
void ht_free(hashtable_t *ht)
{
size_t i;
if (ht)
{
for (i = 0; i < ht->size; i++)
if (ht->tab[i] != NULL)
free_te_list(ht->tab[i]);
free(ht);
}
}
/* resizes hashtable, returns new hashtable and frees old */
static hashtable_t *resize(hashtable_t *oht, size_t size)
{
hashtable_t *nht; /* new hashtable */
nht = ht_create(size, oht->type);
/* rehash */
size_t i;
tableentry_t *te;
/* loop through hashtable */
for (i = 0; i < oht->size; i++)
/* loop through linked list */
for (te = oht->tab[i]; te != NULL; te = te->next)
/* insert & rehash old vals into new ht */
if (ht_insert(nht, te->key, te->val) == NULL)
return NULL;
ht_free(oht);
return nht;
}
hash.h
/* a hash-table implementation in c */
/*
hashing algorithm: hashval = *s + 31 * hashval
resolves collisions using linked lists
*/
#ifndef HASH
#define HASH
typedef struct hashtable hashtable_t;
typedef enum datatype {STRING, INTEGER} datatype_t;
/* inserts the key-val pair */
hashtable_t *ht_insert(hashtable_t *ht, char *k, void *v);
/* creates hashtable */
/* NOTE: dynamically allocated, remember to ht_free() */
hashtable_t *ht_create(size_t size, datatype_t type);
/* frees hashtable created from ht_create() */
void ht_free(hashtable_t *ht);
/* retrive value from key */
void *ht_get(hashtable_t *ht, char *k);
hashtable_t *ht_delete(hashtable_t *ht, char *k);
#endif
Do not use the hash table as the container for the data; only use it to refer to the data, and you won't have that problem.
For example, let's say you have key-value pairs, using a structure with the actual data in the C99 flexible array member:
struct pair {
struct pair *next; /* For hash chaining */
size_t hash; /* For the raw key hash */
/* Payload: */
size_t offset; /* value starts at (data + offset) */
char data[]; /* key starts at (data) */
};
static inline const char *pair_key(struct pair *ref)
{
return (const char *)(ref->data);
}
static inline const char *pair_value(struct pair *ref)
{
return (const char *)(ref->data + ref->offset);
}
Your hash table can then be simply
struct pair_hash_table {
size_t size;
struct pair **entry;
};
If you have struct pair_hash_table *ht, and struct pair *foo with foo->hash containing the hash of the key, then foo should be in the singly-linked list hanging off ht->entry[foo->hash % ht->size];.
Let's say you wish to resize the hash table ht. You choose a new size, and allocate enough memory for that many struct pair *. Then, you go through each singly-linked list in each old hash entry, detaching them from the old list, and prepending them to the lists in correct hash table entries in the new hash table. Then you just free the old hash table entry array, replacing it with the new one:
int resize_pair_hash_table(struct pair_hash_table *ht, const size_t new_size)
{
struct pair **entry, *curr, *next;
size_t i, k;
if (!ht || new_size < 1)
return -1; /* Invalid parameters */
entry = malloc(new_size * sizeof entry[0]);
if (!entry)
return -1; /* Out of memory */
/* Initialize new entry array to empty. */
for (i = 0; i < new_size; i++)
entry[i] = NULL;
for (i = 0; i < ht->size; i++) {
/* Detach the singly-linked list. */
next = ht->entry[i];
ht->entry[i] = NULL;
while (next) {
/* Detach the next element, as 'curr' */
curr = next;
next = next->next;
/* k is the index to this hash in the new array */
k = curr->hash % new_size;
/* Prepend to the list in the new array */
curr->next = entry[k];
entry[k] = curr;
}
}
/* Old array is no longer needed, */
free(ht->entry);
/* so replace it with the new one. */
ht->entry = entry;
ht->size = size;
return 0; /* Success */
}
Note that the hash field in struct pair is not modified, nor recalculated.
Having the raw hash (as opposed to modulo table-size), means you can speed up the key search even when different keys use the same slot:
struct pair *find_key(struct pair_hash_table *ht,
const char *key, const size_t key_hash)
{
struct pair *curr = ht->entry[key_hash % ht->size];
while (curr)
if (curr->hash == key_hash && !strcmp(key, pair_key(next)))
return curr;
else
curr = curr->next;
return NULL; /* Not found. */
}
In C, the logical and operator, &&, is short-circuiting. If the left side is not true, the right side is not evaluated at all, because the entire expression can never be true in that case.
Above, this means that the raw hash value of the key is compared, and only when they do match, the actual strings are compared. If your hash algorithm is even halfway good, this means that if the key already exists, typically only one string comparison is done; and if the key does not exist in the table, typically no string comparisons are done.
You can deal with them the same way the standard library (C++) deals with this exact problem:
Some operations on containers (e.g. insertion, erasing, resizing) invalidate iterators.
For instance std::unordered_map which is basically a hash table implemented with buckets has these rules:
insertion
unordered_[multi]{set,map}: all iterators invalidated when rehashing
occurs, but references unaffected [23.2.5/8]. Rehashing does not occur
if the insertion does not cause the container's size to exceed z * B
where z is the maximum load factor and B the current number of
buckets. [23.2.5/14]
erasure
unordered_[multi]{set,map}: only iterators and references to the
erased elements are invalidated [23.2.5/13]
Iterator invalidation rules
The C++ concept of iterators is a generalization of pointers. So this concept can be applied to C.
Your only other alternative is that instead of holding the objects directly into the container you add another level of indirection and hold some sort of proxy. And so the elements always stay at the same position in memory. It's the proxies that move around on resizing/inserting etc. But you need to analize this scenario: are the added double indirection (which will surely affect performance in a negative way) and increase implementation complexity worth it? Is is that important to have persistent pointers?
I'm pretty new to C, and I don't have a very good grasp of pointers. I'm trying to build a hash table. Here's what I have so far.
I've been cobbling this together from several sources, and I've lost any idea I have of what the pointers are doing. If anyone could even give me a hint as to where my problem is I'd be very grateful.
Header File
typedef struct {
char * word;
char * defn;
struct entry *next;
} entry;
typedef struct {
int size;
struct entry **table;
} hashTable;
typedef hashTable * Dictionary;
Code
#include "hashP.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
Dictionary create(int initial_capacity, int delta_capacity){
Dictionary new_table;
int i;
if ((new_table = malloc(sizeof(Dictionary))) == NULL){
return NULL;
}
if ((new_table->table = malloc(sizeof(entry *) * initial_capacity)) == NULL){
return NULL;
}
for(i=0; i < initial_capacity; i++){
new_table->table[i] = NULL;
}
return new_table;
}
/* Insert a key-value pair into a hash table. */
void insertEntry(Dictionary table, char *index, char *value) {
int bin = 0;
entry *newpair = NULL;
entry *next = NULL;
entry *last = NULL;
unsigned long int hashval;
int i = 0;
char *word = index;
char *defn = value;
/* Convert our string to an integer */
while( hashval < ULONG_MAX && i < strlen(word) ) {
hashval = hashval << 8;
hashval += word[i];
i++;
}
bin = hashval % table->size;
next = table->table[bin];
while( next != NULL && next->word != NULL && strcmp(word, next->word ) > 0 ) {
last = next;
next = next->next;
}
/* There's already a pair. Let's replace that string. */
if( next != NULL && next->word != NULL && strcmp( word, next->word ) == 0 ) {
free( next->defn );
next->defn = strdup(defn);
/* Nope, could't find it. Time to grow a pair. */
} else {
if( ( newpair = malloc( sizeof(entry) ) ) == NULL ) {
return NULL;
}
if( ( newpair->word = strdup(word) ) == NULL ) {
return NULL;
}
if( ( newpair->defn = strdup(defn) ) == NULL ) {
return NULL;
}
newpair->next = NULL;
/* We're at the start of the linked list in this bin. */
if( next == table->table[ bin ] ) {
newpair->next = next;
table->table[bin] = newpair;
/* We're at the end of the linked list in this bin. */
} else if ( next == NULL ) {
last->next = newpair;
/* We're in the middle of the list. */
} else {
newpair->next = next;
last->next = newpair;
}
}
}
Sorry for the huge wall of text. It gives me an "Assignment from incompatible pointer type" every time I use "next".
If you declare your struct as:
struct entry { ... };
Then you should use it as
struct entry* next;
If you declare it as:
typedef struct { ... } entry;
Then you should use it as
entry* next;
In this case struct entry* next will still compile (as in your case), but will refer to an incomplete type, which is not the entry that you defined. Assigning from something that is entry* to something that is struct entry* will, therefore, give you an error.
To fix your problem just replace all occurrences of struct entry with entry.
UPDATE: it will not work, because by the time you define entry* next entry itself is not defined yet. You can fix it like this, for example:
typedef struct entry_t {
char * word;
char * defn;
struct entry_t *next;
} entry;
Beside other problems, Dictionary is not defined. From how you use it, I am guessing it is a typedef for a pointer to a hashTable. The allocation is wrong, you allocate the size for a pointer, not for the hashTable structure. You should write:
Dictionary new_table = malloc(sizeof(*new_table));
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.
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);
}