I'm trying to implement a simple hash table in C, where I have two structures, one for the entries, and one for the table itself:
typedef struct hash_table_entry {
int key;
int data;
struct hash_table_entry *next;
} HT_ENTRY;
typedef struct hash_table {
int size;
int entry_count;
HT_ENTRY **table;
} HT;
My mallocs when creating the table:
if ((ht_table = (HT*)malloc(sizeof(HT))) == NULL)
return NULL;
if ((ht_table->table = (HT_ENTRY**)malloc(size * (sizeof(HT_ENTRY*)))) == NULL)
return NULL;
for (i = 0; i < size; i++) {
if ((ht_table->table[i] = (HT_ENTRY*)malloc(sizeof(HT_ENTRY))) == NULL)
return NULL;
}
When allocating the table, my second malloc doesn't create the array of pointers in the desired size I would like him to do: pic of debugger
Why does my malloc do this and what would be the correct syntax?
You are allocating individual entries in the hash table but there is no data associated to these entries, indeed they are uninitialized, causing undefined behavior when you try and use the hash table. You should instead just initialize the array of pointers pointed to by ht_table->table with NULL pointers:
ht_table->size = size;
ht_table->entry_count = 0;
for (i = 0; i < size; i++) {
ht_table->table[i] = NULL;
}
Or simply allocate the array with calloc():
ht_table->size = size;
ht_table->entry_count = 0;
if ((ht_table->table = (HT_ENTRY**)calloc(size, sizeof(HT_ENTRY*))) == NULL)
return NULL;
Also not that in C, casting the return value of malloc and calloc is not required. A safer way to allocate the array is:
if ((ht_table->table = calloc(size, sizeof(*ht_table->table))) == NULL)
return NULL;
Related
Can we dynamically allocate memory for structures? Is this a correct procedure to approach a dynamically allocated structures? Please tell me how to malloc() and realloc() a structure.
newnode is of type struct List * but when start indexing it converts to struct List.How this conversion possible?My insert function accepts only (struct List*) Am I wrong somewhere?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct List {
char val[20];
};
void insert(struct List *);
int main(void) {
int i = 0;
int size = 1;
struct List *newnode = (struct List *)malloc(size * sizeof(struct List));
for (i = 0; i < 5; i++) {
if (size <= i) {
size = size + size;
newnode = (struct List *)realloc(newnode, size * sizeof(struct List));
}
scanf("%s", newnode[i].val);
insert(newnode[i]);
}
for (i = 0; i < 5; i++) {
printf("%s\n", newnode[i].val);
}
return 0;
}
void insert(struct List *node) {
printf("%s\n", node->val);
}
The code works except for 3 problems:
You do not test for memory allocation failure. Both malloc() and realloc() will return NULL if memory cannot be allocated: you will get undefined behavior when dereferencing newnode should this happen.
to handle the case of realloc() failure gracefully, you should store the reallocated pointer to a different variable so you can still access the previous array that has not been deallocated and free it.
scanf("%s", newnode[i].val); is a security flaw: you should limit the number of bytes that can be stored to the destination array with
scanf("%19s", newnode[i].val);
you do not test the return value of scanf() to detect invalid or missing input.
insert() does not insert anything.
Here is a modified version with error handling and less confusing names:
#include <stdio.h>
#include <stdlib.h>
struct Item {
char val[20];
};
//void insert(struct Item *);
int main(void) {
int i, j;
int size = 0;
struct Item *array = NULL;
for (i = 0; i < 5; i++) {
if (i >= size) {
int newsize = size ? 1 : size + size;
struct Item *newarray = realloc(array, sizeof(*array) * size);
if (newarray == NULL) {
perror("cannot reallocate the array");
break;
}
size = newsize;
array = newarray;
}
if (scanf("%19s", array[i].val) != 1) {
fprintf(stderr, "missing input\n");
break;
}
//insert(array[i]);
}
for (j = 0; j < i; i++) {
printf("%s\n", array[i].val);
}
free(array);
return 0;
}
Yes, this is fine except that you are assigning the return value to your original array pointer. realloc() returns NULL if it can't resize the memory. You need to assign it to a temporary variable and, if the value is NULL, don't overwrite a.
The main thing you have to watch out for are pointers, which your struct doesn't have. In those cases, the memory pointed to is not part of the allocated array.
typedef struct {
List *table;
unsigned int size;
} HashTable;
typedef struct node {
Data data;
struct node *next;
} NODE;
struct listptrs {
NODE *tail;
NODE *head;
NODE *prev;
NODE *current;
};
typedef struct listptrs List;
HashTable createHashTable(unsigned int size) {
//HashTable htable = { 0 };
//return htable;
int i;
HashTable *htable = NULL;
htable = malloc(sizeof(HashTable) * size);
for (i = 0; i < size; i++) {
htable[i].table = malloc(sizeof(List));
htable[i].table->current = NULL;
htable[i].table->head = NULL;
htable[i].table->prev = NULL;
htable[i].table->tail = NULL;
htable[i].size = size;
}
return *htable;//???
}
Then in main:
HashTable htable = createHashTable(tableSize);
htable doesn't act like an array at all. Any ideas how to solve it without changing any return value from the function and arguments for functions? This is part of a school assignment and only the contents of the function createHashTable may be changed. The rest of the program is not here because it isn't relevant to the question.
You maybe want this:
HashTable *createHashTable(unsigned int size)
{
//HashTable htable = { 0 };
//return htable;
int i;
HashTable* htable = NULL;
htable = malloc(sizeof(HashTable)* size);
for(i=0; i<size; i++)
{
htable[i].table = malloc(sizeof(List));
htable[i].table->current = NULL;
htable[i].table->head = NULL;
htable[i].table->prev = NULL;
htable[i].table->tail = NULL;
htable[i].size = size;
}
return htable;
}
As you allocate the array dynamically, you can simply return the newly allocated pointer. Returning a HashTable as you were trying doesn't make senses, because this would allow you to return one single HashTable, but you want to return a whole array of HashTables.
Usage:
Instead of:
HashTable htable = createHashTable(tableSize);
You need this:
HashTable *htable = createHashTable(100);
...
... // when done you need to delete the hashtable
deleteHashTable(htable);
The deleteHashTable is yet to be written, It essentially needs to free the table pointer and to free the table itself.
Now if you really are allowed to change only the contents of the createHashTable function but not the function signature, then your question doesn't make sense because with the function signature HashTable createHashTable(unsigned int size) you can only return one HashTable but not an array of HashTables.
But then maybe you actually want this:
HashTable createHashTable(unsigned int size)
{
HashTable htable = { 0 };
int i;
for(i=0; i<size; i++)
{
htable[i].table = malloc(sizeof(List));
htable[i].table->current = NULL;
htable[i].table->head = NULL;
htable[i].table->prev = NULL;
htable[i].table->tail = NULL;
htable[i].size = size;
}
return htable;
}
With this second solution, you still need to write the function that deletes the hash table.
The hash table itself isn't supposed to "behave like an array", and this:
return *htable;
makes no sense, it returns the first element from your array of hash tables.
You're not supposed to create an array of hash tables though, you're supposed to create a single hash table, which might contain an array (that's the table). It also has a size variable for instance, so there's more than the array itself to the hash table.
You should do
htable = malloc(sizeof *htable);
to allocate a single instance, then initialize that as needed and return it.
There seems to be some confusion here: createHashTable() is not supposed to allocate an array of hash tables, but a HashTable structure with an initial size for its embedded table member.
Furthermore, it is non standard practice to return the structure by value. You should instead return the pointer to the allocated HashTable or possibly take a pointer to HashTable structure allocated dynamically or statically by the caller and initialize that.
Here is a modified version of the code for this approach:
#include <stdlib.h>
typedef struct {
List *table;
unsigned int size;
} HashTable;
typedef struct node {
Data data;
struct node *next;
} NODE;
struct listptrs {
NODE *tail;
NODE *head;
NODE *prev;
NODE *current;
};
typedef struct listptrs List;
HashTable *createHashTable(unsigned int size) {
HashTable *htable = malloc(sizeof(*htable));
if (htable == NULL)
return NULL;
}
htable->size = size;
htable->table = NULL;
if (size == 0) {
return htable;
}
htable->table = malloc(sizeof(*htable->table) * size);
if (htable->table == NULL) {
free(htable);
return NULL;
}
for (unsigned int i = 0; i < size; i++) {
htable->table[i].head = NULL;
htable->table[i].tail = NULL;
htable->table[i].prev = NULL;
htable->table[i].current = NULL;
}
return htable;
}
Calling from main():
HashTable *htable = createHashTable(100);
I have 2 structures in my C code. I want to write a hash function with these 2 structures. So I want to initialize my data null in first case. My code is
struct HashNode
{
char username[20];
char password[20];
};
struct HashTable
{
int size;
struct HashNode *table;
};
HashTable *initializeTable(int size)
{
HashTable *htable;
if (size < MIN_TABLE_SIZE)
{
printf("Table Size Small\n");
return NULL;
}
htable = (HashTable *)malloc(sizeof(Hashtable));
if (htable == NULL)
{
printf("memory allocation pblm\n");
return NULL;
}
htable->size = size;
}
How can I allocate memory for htable->table with that size? I have code in C++ like htable->table = new HashNode [htable->size];. How can I write this in C using malloc?
You can allocate the memory in this way
htable->table = malloc(size*sizeof(HashNode))
I have a pointer to a pointer to a structure and I am trying to access a pointer-to-pointer pointer within that structure. I keep getting an error that reads: "request for member 'buckets' in something not a structure or union." I commented next to the error. So my question is, how do I properly access buckets and allocate memory for it.
typedef struct bucket {
char *key;
void *value;
struct bucket *next;
} Bucket;
typedef struct {
int key_count;
int table_size;
void (*free_value)(void *);
Bucket **buckets;
}
int create_table(Table ** table, int table_size, void (*free_value)(void *)){
int iterate = 0;
*table = malloc(sizeof(Table));
if(table && table_size != 0) {
(*table)->key_count = 0;
(*table)->table_size = table_size;
(*table)->free_value = free_value;
(*table)->buckets = malloc(table_size * sizeof(Bucket)); /* Error is here */
while(iterate < table_size)
*table->buckets[iterate++] = NULL;
return SUCC;
}
return FAIL;
}
By the look of your allocation:
(*table)->buckets = malloc(table_size * sizeof(Bucket));
It looks as though you are attempting to create space for table_size buckets and not table_size pointers to bucket. It the allocation should read:
(*table)->buckets = malloc(table_size * sizeof(Bucket*));
The error, I believe, is further down in the while loop. Operator -> has precedence over [] which has precedence over *, therefore you are actually saying *(table->buckets[iterate++]), and table is a pointer-pointer and does therefore not have a member called bucket.
(Note: You're missing a Table; in your second typedef.
int create_table(Table ** table, int table_size, void (*free_value)(void *)){
int iterate = 0;
*table = malloc(sizeof(Table));
if(table && table_size != 0) {
This isn't right. You should test table before you allocate memory, and then test *table.
(*table)->key_count = 0;
(*table)->table_size = table_size;
(*table)->free_value = free_value;
(*table)->buckets = malloc(table_size * sizeof(Bucket)); /* Error is here */
Here, you are allocating space for table_size number of Bucket, but assigning the memory to a pointer to pointer to Bucket. It looks like you want to allocate table_size number of pointers to Bucket.
while(iterate < table_size)
*table->buckets[iterate++] = NULL;
This is the same as *(table->buckets[iterate++]) which is obviously wrong. table is not a pointer to struct, *table is. This is where your real error is.
I would probably write something like this:
typedef struct {
int key_count;
int table_size;
void (*free_value)(void *);
Bucket **buckets;
} Table;
int create_table(Table **table, int table_size, void (*free_value)(void *))
{
if (!table || table_size == 0) {
return FAIL;
}
*table = malloc(sizeof(Table));
if (*table) {
(*table)->key_count = 0;
(*table)->table_size = table_size;
(*table)->free_value = free_value;
(*table)->buckets = malloc(table_size * sizeof(Bucket *));
if ((*table)->buckets) {
int iterate = 0;
while(iterate < table_size)
(*table)->buckets[iterate++] = NULL;
return SUCC;
}
}
if (*table) {
free ((*table)->buckets);
}
free (*table);
return FAIL;
}
Just trying to make a kind of hash table with each node being a linked list.
Having trouble just initializing the space, what am I doing wrong?
#include <stdlib.h>
typedef struct entry {
struct entry *next;
void *theData;
} Entry;
typedef struct HashTable {
Entry **table;
int size;
} HashTable;
int main(){
HashTable *ml;
ml = initialize();
return 0;
}
HashTable *initialize(void)
{
HashTable *p;
Entry **b;
int i;
if ((p = (HashTable *)malloc(sizeof(HashTable *))) == NULL)
return NULL;
p->size = 101;
if ((b = (Entry **)malloc(p->size * sizeof(Entry **))) == NULL)
return NULL;
p->table = b;
for(i = 0; i < p->size; i++) {
Entry * b = p->table[i];
b->theData = NULL;
b->next = NULL;
}
return p;
}
You need to change sizeof(HashTable*) to sizeof(HashTable) and similarly sizeof(Entry **) to sizeof(Entry *) . And the second thing is for every Entry you need to allocate memory using malloc again inside the loop.
if ((p = malloc(sizeof(HashTable))) == NULL)
return NULL;
p->size = 101;
if ((b = malloc(p->size * sizeof(Entry *))) == NULL)
return NULL;
I believe removing the malloc() result casts is best practice.
Plus, as #Naveen was first to point out you also need to allocate memory for each Entry.
Firstly your sizeofs are wrong. T * = malloc( num * sizeof(T)) is correct. You can also use calloc.
You are reusing b for different purposes so it is quite confusing. Not generally good using a single character variable.
p->table which was b is allocated but not initialised, i.e. it doesn't point to anything useful, then you are trying to dereference it.
You need to fill it will Entry* pointers first, and they must be pointing to valid Entry structs if you are going to dereference those.
Your process probably dies on the line b>theData = NULL
Also, you can statically declare your HashTable, either locally, or in some region high enough in the stack that the stack is non-ascending (in memory) while it is used and pass a pointer to the HashTable to your initialize function to avoid a malloc. malloc is slow.
So in main, you can do:
HashTable table;
InitializeHashTable(&table);
// use table (no need to free)
// just do not return table