Hashtable in C using linked lists - c

I'm new to C been at it about two weeks, hitting a few problems with linked lists and hash tables. The compilier is throwing a few errors:
I've tried to mark these in the source code.
finddupl.c: In function 'main':
finddupl.c:35:5: warning: assignment from incompatible pointer type
finddupl.c:37:3: warning: passing argument 1 of 'ml_lookup' from incompatible pointer type
mlist.h:19:9: note: expected 'struct MList *' but argument is of type 'struct MList *'
mlist.c: In function 'ml_lookup':
mlist.c:63:37: warning: assignment from incompatible pointer type
mlist.c: In function 'ml_add':
mlist.c:78:9: error: request for member 'hashtable' in something not a structure or union
mlist.c:90:12: warning: assignment from incompatible pointer type
Can anyone guide me in the right direction here, I've been at this for a few hours
Not loving C so far :p
I've left out the mentry.c and the c file with the main function, I've tested both of these before I tried to write the hash table.
Mentry.h
#ifndef _MENTRY_INCLUDED_
#define _MENTRY_INCLUDED_
#include <stdio.h>
typedef struct mentry {
char *surname;
int house_number;
char *postcode;
char *full_address;
} MEntry;
/* me_get returns the next file entry, or NULL if end of file*/
MEntry *me_get(FILE *fd);
/* me_hash computes a hash of the MEntry, mod size */
unsigned long me_hash(MEntry *me, unsigned long size);
/* me_print prints the full address on fd */
void me_print(MEntry *me, FILE *fd);
/* me_compare compares two mail entries, returning <0, 0, >0 if
* me1<me2, me1==me2, me1>me2
*/
int me_compare(MEntry *me1, MEntry *me2);
#endif /* _MENTRY_INCLUDED_ */
mlist.h
#ifndef _MLIST_INCLUDED_
#define _MLIST_INCLUDED_
#include "mentry.h"
typedef struct mlist MList;
extern int ml_verbose; /* if true, prints diagnostics on stderr */
/* ml_create - created a new mailing list */
struct MList *ml_create(void);
/* ml_add - adds a new MEntry to the list;
* returns 1 if successful, 0 if error (malloc)
* returns 1 if it is a duplicate */
int ml_add(MList **ml, MEntry *me);
/* ml_lookup - looks for MEntry in the list, returns matching entry or NULL */
MEntry *ml_lookup(struct MList *ml, MEntry *me);
#endif /* _MLIST_INCLUDED_ */
mlist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mentry.h"
#include "mlist.h"
#define HASHSIZE 101
struct Mlist_node{
MEntry *me;
MEntry *next;
int size;
};
struct Mlist_head{
struct Mlist_node *head;
struct Mlist_node *tail;
};
struct MList{
int size;
struct Mlist_head hashtable[HASHSIZE];
};
struct MList *ml_create(void){
struct MList *m;
struct Mlist_head *h;
int i;
if ((m = ( struct MList *)malloc(sizeof(struct MList))) != NULL){
if ((h = (struct Mlist_head *)malloc(sizeof(struct Mlist_head))) != NULL) {
for (i = 0; i < HASHSIZE; i++) {
h = &(m->hashtable[i]);
h->head = NULL;
h->tail = NULL;
}
printf("worked");
return m;
}
}
printf("fail");
return NULL;
}
MEntry *ml_lookup(struct MList *ml, MEntry *me){
struct Mlist_node *mn;
struct Mlist_head *mh;
if ((mn = (struct Mlist_node *)malloc(sizeof(struct Mlist_node))) != NULL) {
if ((mh = (struct Mlist_head *)malloc(sizeof(struct Mlist_head))) != NULL) {
unsigned hashval = me_hash(me,HASHSIZE);
printf("%d",hashval);
mh=&(ml->hashtable[hashval]);
for (mn = mh->head; mn != NULL; mn = mn->next) //LINE 63 ERROR
if (me_compare(mn->me, me) == 0)
return me; /* found */
}
}
return NULL;
}
int ml_add(MList **ml, MEntry *me){
unsigned hashval;
struct Mlist_head *mh;
struct Mlist_node *mn;
hashval = me_hash(me,HASHSIZE);
mh = ml->hashtable[hashval]; //LINE 78 ERROR
if ((mn = (struct Mlist_node *)malloc(sizeof(struct Mlist_node))) != NULL){
mn->me=me;
if(mh->head==NULL){
mh->head=mn;
mh->tail=mn;
mn->next=NULL;
}
else{
mn = mh->tail;
mn->next=me;
mh->tail=me; /LINE 90 ERROR
}
return 1;
}
else{
printf("failed to allocate memory");
return 0;
}
/* not found */
}

Look into how you're forward declaring MList in mlist.h. You use the syntax
typedef struct mlist MList;
This looks a little off to me. You're declaring a type of name MList. Note that with the above style of declaration, the compiler expects to see "struct mlist" or just the type name's MList when referring to this struct. (This question may be helpful to you). However later on you definen a struct MList as follows:
struct MList{
int size;
struct Mlist_head hashtable[HASHSIZE];
};
Which makes the compiler expect afterwords to see things referred to with "struct MList". But in your fwd declaration, you just said that plain old "MList" without the struct is ok. The compiler may or may not be able to make sense of this confusion between the fwd declaration and your definition. Its made worse in that one is a tag name of the struct and the other is the struct's canonical name. This kind of thing makes my spidy sense tingle that something could be off and causing confusion between you and the compiler.
I would just change how you forward declare to be consistent with how Mlist is used and see if that helps you. To do this change the above line to:
struct MList;
(then notice in mlist.h how you inconsistently use the struct keyword when referring to MList. Sometimes you do and sometimes you don't. this may also be causing problems w/ confusing your compiler. If you change to the above fwd declaration, use struct MList, not just MList)
In other news, for:
mlist.c:63:37: warning: assignment from incompatible pointer type
You're assigning a MEntry to a MNode, which are of different types, so I would expect you to get the warning you're getting.

Related

Warning: implicit declaration of function ‘malloc’, even if <stdlib.h> is included

This is an extract of a code, where I populate a list with the elements of an array.
#include <stdlib.h>
#include <stdio.h>
#include "../../lib/kernel/list.h"
#include "./listpop.h"
struct item {
struct list_elem elem;
int value;
int priority;
};
void populate(struct list * l, int * a, int n);
void populate(struct list * l, int * a, int n)
{
int i = 0;
while(i != n) {
struct item * newitem = malloc(sizeof(struct item));
newitem->value = a[i];
list_push_back(l,newitem);
i++;
}
}
void test_assignment_1()
{ struct list our_list;
list_init(&our_list);
populate(&our_list, ITEMARRAY, ITEMCOUNT);
}
Code inside list.h:
/* List element. */
struct list_elem
{
struct list_elem *prev; /* Previous list element. */
struct list_elem *next; /* Next list element. */
};
/* List. */
struct list
{
struct list_elem head; /* List head. */
struct list_elem tail; /* List tail. */
};
void list_init (struct list *);
Code inside list.c:
/* Initializes LIST as an empty list. */
void
list_init (struct list *list)
{
ASSERT (list != NULL);
list->head.prev = NULL;
list->head.next = &list->tail;
list->tail.prev = &list->head;
list->tail.next = NULL;
}
And finally, the code inside listpop.h:
#define ITEMCOUNT 10
int ITEMARRAY[ITEMCOUNT] = {3,1,4,2,7,6,9,5,8,3};
Here are the warnings I get:
warning: implicit declaration of function ‘malloc’
warning: incompatible implicit declaration of built-in function ‘malloc’
So far, all I've read about those warnings is to add stdlib.h, but as you can see from my code I've already done it, and the code still give me those warnings. I've restarted the code many times, so the error lays somewhere in the code.
Anyone knows what is not working here?
You might be compiling on a obsolete system with a non conforming compiler and/or C library. Try including <malloc.h> in addition to <stdlib.h> and always include the standard headers first.

Error compiling hash table implementation in C

I have a file with ~6 million records (key value pairs) that I am trying to store in a hash table. However, I am falling at the first fence. The program will not compile and I have not even attempted to read in the records yet.
Firstly, when trying to initialise the whole array I am getting the following error:
31: error: incompatible types when assigning to type ‘struct node’ from type ‘void *’
Perhaps I should make everything inside my struct node equal to null? But is it not better that the array locations are equal to null?
Also, in my insert function, when checking if the array location is null I get this error:
invalid type argument of unary ‘*’ (have ‘struct node’)
How do I check if the array location is empty? This error is from lines 63, 65, 69, 71, 76
I have read many posts on here (including Why whole structure can not be compared in C, yet it can be copied?) but cannot get my code to compile. Apologies if this is basic stuff.
Full code I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define HASH_SIZE 1000000
struct node{
unsigned long long key;
float value;
struct node *next;
};
struct node *ebv_hash = NULL;
unsigned long long hash(unsigned long long);
void insert(unsigned long long, float);
int main(int argc, char *argv[]){
FILE *ebv_fp;
ebv_fp=fopen(argv[1], "r");
ebv_hash = malloc(HASH_SIZE * sizeof(struct node));
for(unsigned long long a=0; a <=HASH_SIZE; a++){
*ebv_hash[a]=NULL;
}
/* Code to read in data I have not written yet */
fclose(ebv_fp);
free(ebv_hash);
return 0;
}
unsigned long long hash(unsigned long long ani_id){
return (ani_id % HASH_SIZE);
}
void insert(unsigned long long nkey, float nvalue){
struct node *nani = malloc(sizeof(struct node));
unsigned long long hash_ind;
nani->key=nkey;
nani->value=nvalue;
nani->next=NULL;
hash_ind = hash(nkey);
if(ebv_hash[hash_ind] == NULL){
ebv_hash[hash_ind] = nani;
}else{
struct node *p = malloc(sizeof(struct node));
p = ebv_hash[hash_ind];
while(p->next != NULL){
p = p->next;
}
p->next = nani;
}
}
You've declared
struct node* ebv_hash = NULL;
and used
ebv_hash = malloc(HASH_SIZE * sizeof(struct node));
This means that you're going to get an array of struct node.
I think what you want is an array of pointers to struct node. That would look like this:
struct node** ebv_hash = NULL;
ebv_hash = malloc(HASH_SIZE * sizeof(struct node*));
for(int i=0;i<HASH_SIZE;i++)
ebv_hash[i] = NULL;
Then when you need a node i, you malloc yourself one and set the appropriate ebv_hash[i].

dereferencing error in linked list - not typedef error

I can't find my dereferencing error to save my life. As far as I can see I've got all my declarations where they need to be and can't see where I have defined a bad structure.
EDIT: the paste is missing a ton of the code, but it includes all of the code that uses the pointers. everything else is checking to make sure the file is the right file, right location, error handling, etc.
typedef struct list{
int pid;
char *name;
struct list* link;
}LIST;
void begin(FILE *file, char *argv[])
{
struct LIST *root, *p, *tail;
struct utmp x[BUFF];
root = NULL;
char *name;
int c;
int i;
struct utmp hold;
int pid;
tail = malloc(sizeof(LIST));
int logins = 0;
int logouts = 0;
for (i=0;i<=BUFF;i++)
{
c = fread(x, sizeof(struct utmp), BUFF, file);
if (strcmp(x[i].ut_user, argv[1]) == 0)
{
if (x[i].ut_type == 7)
{
hold = x[i];
logins++;
pid = x[i].ut_pid;
name = hold.ut_user;
p = create(pid, name); //<--Line 129
if (root == NULL)
{
root = tail = p;
}
else
{
tail->link = p; //<---Line 136
tail = p;
}
printf("%d\n", p->pid); //<---Line 139
}
}
if (x[i].ut_pid == pid && x[i].ut_type == 8)
{
pid = 0;
logouts++;
}
}
LIST *create(int pid, char *name)
{
LIST *p;
p = malloc(sizeof(LIST));
p->pid = pid;
p->link = NULL;
p->name = name;
return p;
}
The error:
main.c: In function ‘begin’:
main.c:129:10: warning: assignment from incompatible pointer type [enabled by default]
main.c:136:9: error: dereferencing pointer to incomplete type
main.c:139:24: error: dereferencing pointer to incomplete type
Your issue is with confusing how to use structs and typedefs. Your definition
typedef struct list{
...
}LIST;
creates 2 data types the structure struct list and its typedef equivalent LIST. Then when you declare your variables, you use struct LIST, which isn't defined. This is generating all 3 of your errors. The compiler is assuming that you are defining this struct somewhere else. The reason that people use typedef is to make the later use of struct unnecessary.

Pointers and structs

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dictionary.h"
#define HASH_SIZE 100
// prototype
int hash(char *word);
// counter
int counter;
// node
typedef struct
{
char *word;
node *next;
} node;
// hash table
node *hashtable[HASH_SIZE];
bool
load(const char *dictionary)
{
// open the dictionary
FILE *dict = fopen(dictionary, "r");
if(dict == NULL)
{
printf("Could not open %s.\n", dictionary);
return false;
}
// set all values in the hash table to null
for(int i = 0; i < HASH_SIZE; i++)
{
hashtable[i] = NULL;
}
// set the counter to 0
counter = 0;
// iterate through the words in the dictionary
while (!feof(dict))
{
// get word into a string
char gotcha[LENGTH];
fscanf(dict, "%s", gotcha);
// declare a node and allocate memory
node n;
n.word = malloc( strlen(gotcha)*sizeof(char) );
// save the word into the node
strcpy(n.word, gotcha);
// hash the word, baby!
int hash_value = hash(n.word);
// start saving addresses to the hashtable
n.next = hashtable[hash_value];
hashtable[hash_value] = &n;
//test
int len = strlen(n.word);
printf("%s\n", n.word);
printf("%i\n", len);
// that's one more!
counter++;
}
fclose(dict);
return true;
}
I am receiving the following two errors on these two lines of code:
n.next = hashtable[hash_value];
hashtable[hash_value] = &n;
dictionary.c:89:16: error: assignment from incompatible pointer type [-Werror]
dictionary.c:90:31: error: assignment from incompatible pointer type [-Werror]
How do I save pointer values in these two places? I am new to this, so please bear that in mind. :)
In your structure, the type node is not yet defined. Change it to use the structure tag:
typedef struct node
{
char *word;
struct node *next;
} node;
This:
typedef struct
{
char *word;
node *next;
} node;
is a syntax error. The node *next; occurs before the end of the typedef that creates node as a type. If your compiler for some reason accepted this, it probably thinks there are 2 different types called "node" now, which explains why one of them isn't compaible with the other. You should give up on that typedef silliness (structs generally shouldn't be typedef'ed) and just use
struct node
{
char *word;
struct node *next;
};
Define typedef names of structs before defining the structs. This allows mutually referring structs without concern for order and doesn't require inconsistent definitions, sometimes with the struct keyword and sometimes without it. Note that in C++ you can do away with the typedef line entirely:
typedef struct node node;
struct node
{
char* word;
node* next;
};

Passing argument from incompatible pointer type warning

I've been trying to figure out pointers in C most of today, even asked a question earlier, but now I'm stuck on something else. I've got the following code:
typedef struct listnode *Node;
typedef struct listnode {
void *data;
Node next;
Node previous;
} Listnode;
typedef struct listhead *LIST;
typedef struct listhead {
int size;
Node first;
Node last;
Node current;
} Listhead;
#define MAXLISTS 50
static Listhead headpool[MAXLISTS];
static Listhead *headpoolp = headpool;
#define MAXNODES 1000
static Listnode nodepool[MAXNODES];
static Listnode *nodepoolp = nodepool;
LIST *ListCreate()
{
if(headpool + MAXLISTS - headpoolp >= 1)
{
headpoolp->size = 0;
headpoolp->first = NULL;
headpoolp->last = NULL;
headpoolp->current = NULL;
headpoolp++;
return &headpoolp-1; /* reference to old pointer */
}else
return NULL;
}
int ListCount(LIST list)
{
return list->size;
}
Now in a new file I have:
#include <stdio.h>
#include "the above file"
main()
{
/* Make a new LIST */
LIST *newlist;
newlist = ListCreate();
int i = ListCount(newlist);
printf("%d\n", i);
}
When I compile, I get the following warning (the printf statement prints what it should):
file.c:9: warning: passing argument 1 of ‘ListCount’ from incompatible pointer type
Should I be worried about this warning? The code seems to do what I want it to, but I'm obviously very confused about pointers in C. After browsing questions on this site, I found that if I make the argument to ListCount (void *) newlist, I don't get the warning, and I don't understand why, nor what (void *) really does...
Any help would be appreciated, thanks.
You're getting confused because of multiple typedefs. LIST is a type representing a pointer to struct listhead. So, you want your ListCreate function to return a LIST, not a LIST *:
LIST ListCreate(void)
The above says: ListCreate() function will return a pointer to a new list's head if it can.
Then you need to change the return statement in the function definition from return &headpoolp-1; to return headpoolp-1;. This is because you want to return the last available head pointer, and you have just incremented headpoolp. So now you want to subtract 1 from it and return that.
Finally, your main() needs to be update to reflect the above changes:
int main(void)
{
/* Make a new LIST */
LIST newlist; /* a pointer */
newlist = ListCreate();
int i = ListCount(newlist);
printf("%d\n", i);
return 0;
}

Resources